Skip to content

2. Os fundamentos da linguagem Java

2.1. Introdução

Vamos, em primeiro lugar, abordar o Java como uma linguagem de programação tradicional. Abordaremos os objetos mais tarde.

Num programa, existem duas coisas

  • dados
  • as instruções que os manipulam

Geralmente, procuramos separar os dados das instruções:

Image

2.2. Dados Java

O Java utiliza os seguintes tipos de dados:

  • inteiros
  • números de ponto flutuante
  • caracteres e cadeias de caracteres
  • booleanos
  • objetos

2.2.1. Tipos de dados predefinidos

Tipo
Codificação
Intervalo
char
2 bytes
Caractere Unicode
int
4 bytes
[-231, 231 -1]
long
8 bytes
[-263, 263 -1]
byte
1 byte
[-27, 27 -1]
short
2 bytes
[-2(15), 2(15),-1]
float
4 bytes
[3,410⁻³⁸, 3,410³⁸] em valor absoluto
duplo
8 bytes
[1,7 × 10⁻³⁰⁸, 1,7 × 10³⁰⁸] em valor absoluto
booleano
1 bit
verdadeiro, falso
String
referência a objeto
string
Data
referência a objeto
data
Caractere
referência a objeto
caractere
Inteiro
referência a objeto
int
Long
referência a objeto
long
Byte
referência a objeto
byte
Float
referência a objeto
float
duplo
referência a objeto
double
Booleano
referência a objeto
Booleano

2.2.2. Notação de dados literais

inteiro
145, -7, 0xFF (hexadecimal)
duplo
134,789, -45E-18 (-45 × 10⁻¹⁸)
flutuante
134,789F, -45E-18F (-45 × 10⁻¹⁸)
caractere
'A', 'b'
string
"hoje"
booleano
verdadeiro, falso
data
new Date(13,10,1954) (dia, mês, ano)

2.2.3. Declaração de dados

2.2.3.1. Papel das declarações

Um programa manipula dados caracterizados por um nome e um tipo. Estes dados são armazenados na memória. Quando o programa é compilado, o compilador atribui a cada dado um local na memória caracterizado por um endereço e um tamanho. Faz isso utilizando as declarações feitas pelo programador.

Além disso, estas declarações permitem ao compilador detetar erros de programação. Assim, a operação

x=x*2;

será declarada incorreta se x for uma cadeia de caracteres, por exemplo.

2.2.3.2. Declaração de Constantes

A sintaxe para declarar uma constante é a seguinte:

<mark style="background-color: #ffff00">tipo</mark>**<mark style="background-color: #ffff00"> final </mark>**<mark style="background-color: #ffff00">nome=valor; </mark>          //define constante nome=valor

ex: final float PI=3.141592F;

Nota

Por que declarar constantes?

  • O programa será mais fácil de ler se for atribuído um nome significativo à constante:
ex: *final float VAT\_rate=0.186F*;
  • A modificação do programa será mais fácil se for necessário alterar a «constante». Assim, no caso anterior, se a taxa de IVA passar para 33%, a única modificação necessária será alterar a instrução que define o seu valor:
*final float taxa\_imposto=0.33F*;

Se tivéssemos utilizado explicitamente 0,186 no programa, teríamos então de modificar inúmeras instruções.

2.2.3.3. Declaração de variáveis

Uma variável é identificada por um nome e refere-se a um tipo de dados. Um nome de variável Java é composto por n caracteres, sendo que o primeiro deve ser uma letra e os restantes podem ser letras ou números. O Java distingue entre letras maiúsculas e minúsculas. Assim, as variáveis FIN e fin são diferentes.

As variáveis podem ser inicializadas quando são declaradas. A sintaxe para declarar uma ou mais variáveis é:

identificateur_de_type variable1,variable2,...,variablen;

onde identificador_de_tipo é um tipo predefinido ou um tipo de objeto definido pelo programador.

2.2.4. Conversões entre números e cadeias de caracteres

número -> cadeia
"" + número
cadeia -> int
Integer.parseInt(cadeia de caracteres)
cadeia -> long
Long.parseLong(cadeia)
string -> double
Double.valueOf(string).doubleValue()
string -> float
Float.valueOf(string).floatValue()

Aqui está um programa que demonstra as principais técnicas para converter entre números e cadeias de caracteres. A conversão de uma cadeia de caracteres num número pode falhar se a cadeia não representar um número válido. Isto resulta num erro fatal, conhecido como exceção em Java. Este erro pode ser tratado utilizando o seguinte bloco try/catch:

try{
    appel de la fonction susceptible de générer l'exception
} catch (Exception e){
    traiter l'exception e
}
instruction suivante

Se a função não lançar uma exceção, o programa prossegue para a instrução seguinte; caso contrário, entra no corpo da cláusula catch e, em seguida, prossegue para a instrução seguinte. Voltaremos ao tratamento de exceções mais tarde.


import java.io.*;
 
public class conv1{
 
  public static void main(String arg[]){
    String S;
    final int i=10;
    final long l=100000;
    final float f=(float)45.78;
    double d=-14.98;
 
    // number --> string
    S=""+i;
    affiche(S);
    S=""+l;
    affiche(S);
    S=""+f;
    affiche(S);
    S=""+d;
    affiche(S);
 
    //boolean --> string
    final boolean b=false;
    S=""+new Boolean(b);
    affiche(S);
 
    // string --> int
    int i1;
    i1=Integer.parseInt("10");
    affiche(""+i1);
    try{
      i1=Integer.parseInt("10.67");
      affiche(""+i1);
    } catch (Exception e){
      affiche("Erreur "+e);
    }
 
    // string --> long
    long l1;
    l1=Long.parseLong("100");
    affiche(""+l1);
    try{
      l1=Long.parseLong("10.675");
      affiche(""+l1);
    } catch (Exception e){
      affiche("Erreur "+e);
    }
    
    // chain --> double
    double d1;
    d1=Double.valueOf("100.87").doubleValue();
    affiche(""+d1);
    try{
      d1=Double.valueOf("abcd").doubleValue();
      affiche(""+d1);
    } catch (Exception e){
      affiche("Erreur "+e);
    }
 
    // string --> float
    float f1;
    f1=Float.valueOf("100.87").floatValue();
    affiche(""+f1);
    try{
      d1=Float.valueOf("abcd").floatValue();
      affiche(""+f1);
    } catch (Exception e){
      affiche("Erreur "+e);
    }
 
}// fine hand
 
  public static void affiche(String S){
    System.out.println("S="+S);
  }
}// end of class

Os resultados são os seguintes:

S=10
S=100000
S=45.78
S=-14.98
S=false
S=10
S=Erreur java.lang.NumberFormatException: 10.67
S=100
S=Erreur java.lang.NumberFormatException: 10.675
S=100.87
S=Erreur java.lang.NumberFormatException: abcd
S=100.87
S=Erreur java.lang.NumberFormatException: abcd

2.2.5. Matrizes de dados

Uma matriz Java é um objeto que permite agrupar dados do mesmo tipo sob um único identificador. É declarada da seguinte forma:

Tipo Matriz[] = new Tipo[n] ou Tipo[] Matriz = new Tipo[n]

Ambas as sintaxes são válidas. n é o número de elementos que a matriz pode conter. A sintaxe Array[i] refere-se ao elemento no índice i, onde i está no intervalo [0,n-1]. Qualquer referência ao elemento Array[i] em que i não esteja no intervalo [0,n-1] causará uma exceção.

Uma matriz bidimensional pode ser declarada da seguinte forma:

Type Array[][] = new Type[n][p] ou Type[][] Array = new Type[n][p]

A sintaxe Array[i] refere-se ao elemento de dados i de Array, onde i pertence ao intervalo [0,n-1]. Array[i] é, por si só, uma matriz: Array[i][j] refere-se ao j-ésimo elemento de Array[i], onde j pertence ao intervalo [0,p-1]. Qualquer referência a um elemento de Array com índices incorretos gera um erro fatal.

Eis um exemplo:


public class test1{
 
  public static void main(String arg[]){
    float[][] taux=new float[2][2];
    taux[1][0]=0.24F;
    taux[1][1]=0.33F;
    System.out.println(taux[1].length);
    System.out.println(taux[1][1]);
  }
}

e os resultados da execução:

2
0.33

Uma matriz é um objeto com um atributo «length»: este é o tamanho da matriz.

2.3. Comandos básicos de Java

Distinguimos

  • as instruções básicas executadas pelo computador.
  • instruções que controlam o fluxo do programa.

As instruções básicas tornam-se claras quando se considera a estrutura de um microcomputador e dos seus periféricos.

Image

  1. Leitura de informações a partir do teclado

  2. Processamento de informação

  3. Gravação de informações no ecrã

  4. Ler informações de um ficheiro em disco

  5. Gravar informações num ficheiro em disco

2.3.1. Escrever no ecrã

A sintaxe da instrução de saída para o ecrã é a seguinte:

System.out.println(expressão) ou System.err.println(expressão)

onde expressão é qualquer tipo de dados que possa ser convertido numa cadeia de caracteres para ser apresentado no ecrã. No exemplo anterior, vimos duas instruções de impressão:

System.out.println(taux[1].length);
System.out.println(taux[1][1]);

O System.out escreve num ficheiro de texto, que por predefinição é o ecrã. O mesmo se aplica ao System.err. A estes ficheiros são atribuídos os números (ou descritores) 1 e 2, respetivamente. O fluxo de entrada do teclado (System.in) também é tratado como um ficheiro de texto, com o descritor 0. Tanto o DOS como o Unix suportam o encadeamento de comandos:

    commande1 | commande2

Tudo o que o comando1 escreve no System.out é canalizado (redirecionado) para a entrada System.in do comando2. Por outras palavras, o comando2 lê no System.in os dados produzidos pelo comando2 através do System.out, que, por isso, já não são apresentados no ecrã. Este sistema é amplamente utilizado no Unix. Neste encadeamento, o fluxo System.err não é redirecionado: ele escreve no ecrã. É por isso que é utilizado para escrever mensagens de erro (daí o seu nome err): podemos ter a certeza de que, quando os comandos são encadeados, as mensagens de erro continuarão a aparecer no ecrã. Devemos, portanto, adquirir o hábito de escrever mensagens de erro no ecrã utilizando o fluxo System.err em vez do fluxo System.out.

2.3.2. Ler dados digitados no teclado

O fluxo de dados proveniente do teclado é representado pelo objeto System.in, do tipo InputStream. Este tipo de objeto permite que os dados sejam lidos caractere a caractere. Cabe ao programador extrair as informações relevantes desse fluxo de caracteres. O tipo InputStream não permite que uma linha de texto seja lida de uma só vez. O tipo BufferedReader permite isso através do método readLine.

Para ler linhas de texto introduzidas através do teclado, criamos um novo fluxo de entrada do tipo BufferedReader a partir do fluxo de entrada System.in* do tipo InputStream*:

BufferedReader IN=new BufferedReader(new InputStreamReader(System.in));

Não explicaremos aqui os detalhes desta instrução, uma vez que envolve o conceito de construção de objetos. Iremos utilizá-la tal como está.

A criação de um fluxo pode falhar: é então gerado um erro fatal, chamado de exceção em Java. Sempre que um método é suscetível de gerar uma exceção, o compilador Java exige que esta seja tratada pelo programador. Portanto, para criar o fluxo de entrada anterior, temos de escrever, na verdade:


BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
        System.err.println("Erreur " +e);
        System.exit(1);
}

Mais uma vez, não entraremos em detalhes sobre o tratamento de exceções aqui. Assim que o fluxo IN anterior tiver sido criado, podemos ler uma linha de texto utilizando a instrução:

    String ligne;
    ligne=IN.readLine();

A linha digitada no teclado é armazenada na variável ligne e pode então ser utilizada pelo programa.

2.3.3. Exemplo de Entrada/Saída

Aqui está um programa que ilustra operações de entrada/saída do teclado/ecrã:


import java.io.*;        // required to use I/O streams
 
public class io1{
  
  
  public static void main (String[] arg){
    
    // write to System.out stream
    Object obj=new Object();
    System.out.println(""+obj);
    System.out.println(obj.getClass().getName());
    
    // write to System.err stream
    int i=10;
    System.err.println("i="+i);
    
    // reading a line entered on the keyboard
    String ligne;
    BufferedReader IN=null;
    try{
      IN=new BufferedReader(new InputStreamReader(System.in));
    } catch (Exception e){
      affiche(e);
      System.exit(1);
    }
    System.out.print("Tapez une ligne : ");
    try{
      ligne=IN.readLine();
      System.out.println("ligne="+ligne);
    } catch (Exception e){
      affiche(e);
      System.exit(2);
    }
  }//fine hand
 
  public static void affiche(Exception e){
    System.err.println("Erreur : "+e);
  }
 
}//end of class

e os resultados da execução:

C:\Serge\java\bases\iostream>java io1
java.lang.Object@1ee78b
java.lang.Object
i=10
Tapez une ligne : je suis là
ligne=je suis là

As instruções

Object obj=new Object();
System.out.println(""+obj);
System.out.println(obj.getClass().getName());

destinam-se a mostrar que qualquer objeto pode ser exibido. Não tentaremos aqui explicar o significado do que é exibido. Temos também a exibição do valor de um objeto no bloco:

try{
      IN=new BufferedReader(new InputStreamReader(System.in));
    } catch (Exception e){
      affiche(e);
      System.exit(1);
    }

A variável e é um objeto Exception que é exibido aqui através da chamada display(e). Encontrámos esta exibição do valor de uma exceção no programa de conversão visto anteriormente, embora não o tenhamos discutido na altura.

2.3.4. Atribuir o valor de uma expressão a uma variável

Aqui, estamos interessados na operação variável=expressão;

A expressão pode ser dos seguintes tipos: aritmética, relacional, booleana, cadeia de caracteres

2.3.4.1. Interpretação da operação de atribuição

A operação variável=expressão; é, ela própria, uma expressão cuja avaliação decorre da seguinte forma:

  • O lado direito da atribuição é avaliado: o resultado é um valor V.
  • O valor V é atribuído à variável
  • O valor V é também o valor da atribuição, agora considerada como uma expressão.

É por isso que a expressão V1=V2=expressão é válida. Devido à precedência, o operador = mais à direita será avaliado. Temos, portanto, V1=(V2=expressão). A expressão V2=expressão é avaliada e tem o valor V. A avaliação desta expressão fez com que V fosse atribuído a V2. O operador = seguinte é então avaliado como V1=V. O valor desta expressão continua a ser V. A sua avaliação faz com que V seja atribuído a V1. Assim, a operação V1=V2=expressão é uma expressão cuja avaliação

1: faz com que o valor de expressão seja atribuído às variáveis V1 e V2

2: retorna o valor de expressão como resultado.

Podemos generalizar isto para uma expressão da forma: V1=V2=....=Vn=expressão

2.3.4.2. Expressão aritmética

Os operadores para expressões aritméticas são os seguintes:

+: adição

  • : subtração

*: multiplicação

/ : divisão: o resultado é o quociente exato se pelo menos um dos operandos for real. Se ambos os operandos forem inteiros, o resultado é o quociente inteiro. Assim, 5/2 -> 2 e 5,0/2 -> 2,5.

% : divisão: o resultado é o resto, independentemente da natureza dos operandos, sendo o quociente um número inteiro. Trata-se, portanto, da operação módulo.

Existem várias funções matemáticas:

double sqrt(double x)
raiz quadrada
double cos(double x)
cosseno
double sin(double x)
seno
tan(x)
Tangente
double potência(double x, double y)
x elevado a y (x > 0)
exp(x)
Exponencial
double log(double x)
Logaritmo natural
double abs(double x)
valor absoluto

etc...

Todas estas funções estão definidas numa classe Java chamada Math. Ao utilizá-las, deve antepor-lhes o nome da classe em que estão definidas. Assim, escreveria:

double x, y=4;
x=Math.sqrt(y);

A definição da classe Math é a seguinte:


public  final  class  java.lang.Math
    extends  java.lang.Object  (I-§1.12)
{
        // Fields
    public final static double E;    §1.10.1
    public final static double PI;    §1.10.2
 
        // Methods
    public static double abs(double  a);    §1.10.3
    public static float abs(float  a);    §1.10.4
    public static int abs(int  a);    §1.10.5
    public static long abs(long  a);    §1.10.6
    public static double acos(double  a);    §1.10.7
    public static double asin(double  a);    §1.10.8
    public static double atan(double  a);    §1.10.9
    public static double atan2(double  a, double  b);    §1.10.10
    public static double ceil(double  a);    §1.10.11
    public static double cos(double  a);    §1.10.12
    public static double exp(double  a);    §1.10.13
    public static double floor(double  a);    §1.10.14
    public static double                        §1.10.15
        IEEEremainder(double  f1, double  f2);
    public static double log(double  a);    §1.10.16
    public static double max(double  a, double  b);    §1.10.17
    public static float max(float  a, float  b);    §1.10.18
    public static int max(int  a, int  b);    §1.10.19
    public static long max(long  a, long  b);    §1.10.20
    public static double min(double  a, double  b);     §1.10.21
    public static float min(float  a, float  b);    §1.10.22
    public static int min(int  a, int  b);    §1.10.23
    public static long min(long  a, long  b);    §1.10.24
    public static double pow(double  a, double  b);    §1.10.25
    public static double random();    §1.10.26
    public static double rint(double  a);    §1.10.27
    public static long round(double  a);    §1.10.28
    public static int round(float  a);    §1.10.29
    public static double sin(double  a);    §1.10.30
    public static double sqrt(double  a);    §1.10.31
    public static double tan(double  a);    §1.10.32
}

2.3.4.3. Operadores na avaliação de expressões aritméticas

A precedência dos operadores na avaliação de uma expressão aritmética é a seguinte (da mais alta para a mais baixa):

[funções], [ ( )], [ *, /, %], [+, -]

Os operadores dentro do mesmo bloco [ ] têm a mesma precedência.

2.3.4.4. Operadores relacionais

Os operadores são os seguintes: <, <=, ==, !=, >, >=

Ordem de precedência

       &gt;, &gt;=, &lt;, &lt;=

       ==, !=

O resultado de uma expressão relacional é o valor booleano falso se a expressão for falsa; caso contrário, é verdadeiro.

Exemplo:

      boolean fin;
      int x;
      fin=x>4;

Comparação de dois caracteres

Suponha que existam dois caracteres C1 e C2. Estes podem ser comparados utilizando os operadores

&lt;, &lt;=, ==, !=, &gt;, &gt;=

Neste caso, os seus códigos ASCII — que são números — são comparados. Recorde-se que, de acordo com a ordem ASCII, verificam-se as seguintes relações:

espaço < .. < '0' < '1' < .. < '9' < .. < 'A' < 'B' < .. < 'Z' < .. < 'a' < 'b' < .. < 'z'

Comparação de duas cadeias de caracteres

São comparadas caractere a caractere. A primeira desigualdade encontrada entre dois caracteres resulta numa desigualdade na mesma direção para as cadeias.

Exemplos:

Considere a comparação das cadeias de caracteres «Cat» e «Dog»

Esta última desigualdade permite-nos concluir que «Cat» < «Dog».

Image

Considere comparar as cadeias "Cat" e "Kitten". São iguais em toda a sua extensão até que a cadeia "Cat" se esgote. Neste caso, a cadeia esgotada é declarada como a "menor". Temos, portanto, a relação

«Cat» &lt; «Kitten».

Funções para comparar duas cadeias de caracteres

Não podemos usar os operadores relacionais <, <=, ==, !=, >, >= aqui. Temos de usar métodos da classe String:

String chaine1, chaine2;
chaine1=;
chaine2=;
int i=chaine1.compareTo(chaine2);
boolean egal=chaine1.equals(chaine2)

No código acima, a variável i terá o valor:

    0: se as duas cadeias forem iguais

    1: se a string 1 &gt; string 2

    -1: se a string 1 for menor que a string 2

A variável equal terá o valor true se as duas cadeias forem iguais.

2.3.4.5. Expressões booleanas

Os operadores são & (e), || (ou) e ! (não). O resultado de uma expressão booleana é um valor booleano.

Ordem de precedência ! , &&, ||

exemplo:

int fin;
int x;
fin= x>2 && x<4;

Os operadores relacionais têm precedência sobre os operadores && e ||.

2.3.4.6. Operações bit a bit

Os operadores

Sejam i e j dois números inteiros.

i<<n
desloca i n bits para a esquerda. Os bits resultantes são zeros.
i>>n
desloca i n bits para a direita. Se i for um inteiro com sinal (signed char, int, long), o bit de sinal é preservado.
i & j
Realiza a operação lógica AND entre i e j, bit a bit.
i | j
Realiza a operação lógica OR entre i e j, bit a bit.
~i
complementa i para 1
i^j
realiza o XOR de i e j

Seja

    int i=0x123F, k=0xF123;
    unsigned j=0xF123;
operação
valor
i<<4
0x23F0
i>>4
0x0123 o bit de sinal é preservado.
k>>4
0xFF12 o bit de sinal é preservado.
i&j
0x1023
i|j
0xF33F
~i
0xEDC0

2.3.4.7. Combinação de operadores

   a=a+b pode ser escrito como a+=b

   a=a-b pode ser escrito como a-=b

O mesmo se aplica aos operadores /, %, *, <<, >>, &, |, ^

Assim, a=a+2; pode ser escrito como a+=2;

2.3.4.8. Operadores de incremento e decremento

A notação variable++ significa variable=variable+1 ou variable+=1

A notação variable-- significa variable=variable-1 ou variable-=1.

2.3.4.9. O operador ?

A expressão expr_cond ? expr1:expr2 é avaliada da seguinte forma:

1: A expressão expr_cond é avaliada. Trata-se de uma expressão condicional com um valor verdadeiro ou falso

2: Se for verdadeiro, o valor da expressão é o de expr1. expr2 não é avaliada.

3: Se for falso, ocorre o contrário: o valor da expressão é o de expr2. expr1 não é avaliada.

Exemplo

*i = (j &gt; 4 ? j + 1 : j - 1);*

atribuirá à variável i:

j+1 se j>4, j-1 caso contrário

Isto é o mesmo que escrever if(j>4) i=j+1; else i=j-1; mas é mais conciso.

2.3.4.10. Ordem de precedência geral dos operadores

() [] função
gd
! ~ ++ --
dg
novos operadores de conversão de tipos
dg
* / %
gd
+ -
gd
<< >>
gd
< <= > >= instanceof
gd
== !=
gd
&
gd
^
gd
|
gd
&&
gd
||
gd
? :
dg
= += -= etc. .
dg

gd: indica que, para operadores de igual precedência, é observada a precedência da esquerda para a direita. Isto significa que, quando uma expressão contém operadores da mesma precedência, o operador mais à esquerda na expressão é avaliado primeiro. dg indica precedência da direita para a esquerda.

2.3.4.11. Conversão de tipos

É possível, dentro de uma expressão, alterar temporariamente a representação de um valor. Isto é chamado de conversão de tipos. A sintaxe para alterar o tipo de um valor numa expressão é (tipo) valor. O valor assume então o tipo especificado. Isto resulta numa alteração na representação do valor.

Exemplo:

int i, j;
float isurj;
isurj= (float)i/j;   // priorité de () sur /

Aqui, é necessário converter i ou j para um tipo de ponto flutuante; caso contrário, a divisão retornará um quociente inteiro em vez de um valor de ponto flutuante.

     *i* é um valor codificado exatamente em 2 bytes

     *(float) i* é o mesmo valor, codificado aproximadamente como um número de ponto flutuante em 4 bytes

Existe, portanto, uma conversão de tipo do valor de i. Esta conversão ocorre apenas durante o cálculo; a variável i mantém sempre o seu tipo int.

2.4. Instruções de controlo de fluxo do programa

2.4.1. Pare

O método exit definido na classe System permite-lhe interromper a execução de um programa.

Sintaxe : void exit(int status)

Ação: interrompe o processo atual e devolve o valor do estado ao processo pai

exit encerra o processo atual e devolve o controlo ao processo de chamada. O valor de estado pode ser utilizado pelo processo de chamada. No DOS, esta variável de estado é devolvida ao DOS na variável de sistema ERRORLEVEL, cujo valor pode ser verificado num ficheiro batch. No Unix, a variável $? recupera o valor de estado se o interpretador de comandos for o Bourne Shell (/bin/sh).

Exemplo:

    System.exit(0);

para encerrar o programa com um valor de estado de 0.

2.4.2. Estrutura condicional simples


 syntaxe :  if (condition) {actions_condition_vraie;} else {actions_condition_fausse;}

notas:

  • A condição é colocada entre parênteses.
  • Cada ação é terminada por um ponto e vírgula.
  • As chaves não são seguidas de ponto e vírgula.
  • As chaves só são necessárias se houver mais do que uma ação.
  • A cláusula else pode ser omitida.
  • Não existe «then».

O equivalente algorítmico desta estrutura é a estrutura if-then-else:

si condition
  alors actions_condition_vraie
  sinon actions_conditions_fausse
finsi

exemplo


    if (x>0)  { nx=nx+1;sx=sx+x;} else dx=dx-x;

É possível aninhar estruturas de decisão:

if(condition1)
if (condition2)
        {......}
      else         //condition2
         {......}
else         //condition1
     {.......}

Por vezes, surge o seguinte problema:


public static void main(void){
      int n=5;
 
   if(n>1)
     if(n>6)
             System.out.println(">6");
     else System.out.println("<=6");
}

No exemplo anterior, a que instrução if se refere o else? A regra é que um else se refere sempre à instrução *if mais próxima: if(n&gt;6)* neste exemplo. Vejamos outro exemplo:


public static void main(void)
{  int n=0;
 
   if(n>1)
     if(n>6)   System.out.println(">6");
     else;            // else from if(n>6): nothing to do 
   else System.out.println("<=1");    // else du if(n>1) 
}

Aqui, queríamos colocar um else na instrução if(n>1) e nenhum else na instrução if(n>6). Devido à nota anterior, somos obrigados a colocar um else na instrução if(n>6), na qual não há nenhuma instrução.

2.4.3. Estrutura de caso

A sintaxe é a seguinte:

switch(expression) {
    case v1:     
            actions1;
            break;
    case v2:     
            actions2;
            break;
         . .. .. .. .. ..
    default:     actions_sinon;
}

notas

  • O valor da expressão de controlo só pode ser um número inteiro ou um caractere.
  • A expressão de controlo é colocada entre parênteses.
  • A cláusula padrão pode ser omitida.
  • Os valores vi são valores possíveis da expressão. Se a expressão for avaliada como vi, as ações que se seguem à cláusula case vi são executadas.
  • A instrução break sai da estrutura case. Se estiver ausente no final do bloco de instruções para o valor vi, a execução continua com as instruções para o valor vi+1.

exemplo

Em algoritmos

selon la valeur de choix
cas 0
          arrêt
      cas 1
    exécuter module M1
  cas 2
    exécuter module M2
  sinon
    erreur<--vrai
findescas

Em Java


    int choix, erreur;
    switch(choix){   
        case 0: System.exit(0);
      case 1: M1();break;
      case 2: M2();break;
      default: erreur=1;
     }

2.4.4. Estrutura de loop

2.4.4.1. Número conhecido de repetições

Sintaxe


    for (i=id;i<=if;i=i+ip){ 
       actions; 
      } 

Notas

  • Os três argumentos do ciclo for estão entre parênteses.
  • Os três argumentos do ciclo for são separados por ponto e vírgula.
  • Cada ação no ciclo for é terminada por um ponto e vírgula.
  • A chave só é necessária se houver mais do que uma ação.
  • A chave não é seguida de ponto e vírgula.

O equivalente algorítmico é a estrutura «for»:

pour i variant de id à if avec un pas de ip
        actions
finpour

o que pode ser traduzido numa estrutura while:

    i  id
    tantque i<=if
        actions
        i i+ip
    fintantque

2.4.4.2. Número de repetições desconhecido

Existem muitas estruturas de controlo em Java para este caso.

Loop while


    while(condition){
          actions;
        } 

O ciclo continua enquanto a condição for verdadeira. O ciclo pode nunca ser executado.

Notas:

  • A condição está entre parênteses.
  • Cada ação é terminada por um ponto e vírgula.
  • As chaves só são necessárias se houver mais do que uma ação.
  • A chave não é seguida de ponto e vírgula.

A estrutura algorítmica correspondente é a estrutura while:

tantque condition
        actions
fintantque

Estrutura do-while

A sintaxe é a seguinte:


    do{
       instructions;
    }while(condition); 

O ciclo continua até que a condição se torne falsa ou enquanto a condição for verdadeira. Aqui, o ciclo é executado pelo menos uma vez.

Notas

  • A condição está entre parênteses.
  • Cada ação é terminada por um ponto e vírgula.
  • As chaves só são necessárias se houver mais do que uma ação.
  • A chave não é seguida de ponto e vírgula.

A estrutura algorítmica correspondente é a estrutura «repeat ... until»:

répéter
    actions
jusqu'à condition

Estrutura para o geral (para)

A sintaxe é a seguinte:


for(instructions_départ;condition;instructions_fin_boucle){
    instructions; 
}

O ciclo continua enquanto a condição for verdadeira (avaliada antes de cada iteração). As instruções_iniciais são executadas antes de entrar no ciclo pela primeira vez. As instruções_finais_do_ciclo são executadas após cada iteração.

Notas

  • Os três argumentos do ciclo for são colocados entre parênteses.
  • Os três argumentos do ciclo for são separados por ponto e vírgula.
  • Cada instrução for é terminada por um ponto e vírgula.
  • A chave só é necessária se houver mais do que uma ação.
  • A chave não é seguida de um ponto e vírgula.
  • As várias instruções em start_statements e end_loop_statements são separadas por vírgulas.

A estrutura algorítmica correspondente é a seguinte:

instructions_départ
tantque condition
        actions
        instructions_fin_boucle
fintantque

Exemplos

Todos os programas a seguir calculam a soma dos primeiros n números inteiros.


1  for(i=1, somme=0;i<=n;i=i+1)
        somme=somme+a[i];
 
2     for (i=1, somme=0;i<=n;somme=somme+a[i], i=i+1);
 
3    i=1;somme=0;
    while(i<=n)
       { somme+=i; i++; }
 
4    i=1; somme=0;
    do somme+=i++;
         while (i<=n);

Instruções de controlo de loop

break
Sai do ciclo for, while ou do...while.
continue
avança para a próxima iteração dos loops for, while e do...while

2.5. A estrutura de um programa Java

Um programa Java que não utilize classes ou funções definidas pelo utilizador, além da função main, pode ter a seguinte estrutura:


public class test1{
    public static void main(String arg[]){
        … code du programme
    }// hand
}// class

A função main, também conhecida como método, é a primeira a ser executada ao executar um programa Java. Deve ter a seguinte assinatura:


    public static void main(String arg[]){

ou


    public static void main(String[] arg){

O nome do argumento arg pode ser qualquer coisa. Trata-se de uma matriz de cadeias de caracteres que representa os argumentos da linha de comandos. Voltaremos a este assunto mais tarde.

Se utilizar funções que possam lançar exceções que não deseja tratar explicitamente, pode envolver o código do programa num bloco try/catch:


public class test1{
    public static void main(String arg[]){
        try{
        … code du programme
        } catch (Exception e){
            // error handling
        }// try
    }// hand
}// class

No início do código-fonte e antes da definição da classe, é comum encontrar instruções de importação de classes. Por exemplo:


import java.io.*;
public class test1{
    public static void main(String arg[]){
        … code du programme
    }// hand
}// class

Vamos ver um exemplo. Considere a seguinte instrução de escrita:

System.out.println("java");

que imprime "java" no ecrã. Há muita coisa a acontecer nesta instrução simples:

  • System é uma classe cujo nome completo é java.lang.System
  • out é uma propriedade desta classe do tipo java.io.PrintStream, outra classe
  • println é um método da classe java.io.PrintStream.

Não vamos complicar desnecessariamente esta explicação, que surge demasiado cedo, uma vez que requer uma compreensão do conceito de classe que ainda não foi abordado. Uma classe pode ser considerada como um recurso. Aqui, o compilador precisará de aceder às classes java.lang.System e java.io.PrintStream. As centenas de classes Java estão organizadas em arquivos também conhecidos como pacotes. As instruções import colocadas no início do programa são usadas para indicar ao compilador quais classes externas o programa necessita (aquelas usadas, mas não definidas no ficheiro fonte a ser compilado). Assim, no nosso exemplo, o nosso programa necessita das classes java.lang.System e java.io.PrintStream. Especificamos isso com a instrução import. Poderíamos escrever no início do programa:

import java.lang.System;
import java.io.PrintStream;

Uma vez que um programa Java utiliza normalmente dezenas de classes externas, seria tedioso escrever todas as instruções de importação necessárias. As classes foram agrupadas em pacotes, e podemos importar o pacote na totalidade. Assim, para importar os pacotes java.lang e java.io, escrevemos:

import java.lang.*;
import java.io.*;

O pacote java.lang contém todas as classes base do Java e é importado automaticamente pelo compilador. Por isso, em última análise, basta escrever:

import java.io.*;

2.6. Tratamento de exceções

Muitas funções Java são capazes de gerar exceções, ou seja, erros. Já nos deparámos com uma função desse tipo, a função readLine:


String ligne=null;
try{
      ligne=IN.readLine();
      System.out.println("ligne="+ligne);
    } catch (Exception e){
      affiche(e);
      System.exit(2);
}// try

Quando uma função é suscetível de lançar uma exceção, o compilador Java exige que o programador a trate, a fim de produzir programas mais resistentes a erros: deve evitar sempre que uma aplicação «trave» inesperadamente. Aqui, a função readLine lança uma exceção se não houver nada para ler — por exemplo, porque o fluxo de entrada foi fechado. O tratamento de exceções segue este padrão:

try{
    appel de la fonction susceptible de générer l'exception
} catch (Exception e){
    traiter l'exception e
}
instruction suivante

Se a função não lançar uma exceção, o programa passa para a instrução seguinte; caso contrário, entra no corpo da cláusula catch e, em seguida, passa para a instrução seguinte. Tenha em atenção os seguintes pontos:

  • e é um objeto derivado do tipo Exception. Podemos ser mais específicos utilizando tipos como IOException, SecurityException, ArithmeticException, etc.: existem cerca de vinte tipos de exceções. Ao escrever catch (Exception e), indicamos que queremos tratar todos os tipos de exceções. Se for provável que o código no bloco try gere vários tipos de exceções, talvez seja melhor sermos mais específicos, tratando a exceção com vários blocos catch:

try{
    appel de la fonction susceptible de générer l'exception
} catch (IOException e){
    traiter l'exception e
}
} catch (ArrayIndexOutOfBoundsException e){
    traiter l'exception e
}
} catch (RunTimeException e){
    traiter l'exception e
}
instruction suivante
  • Pode ser adicionada uma cláusula finally aos blocos try/catch:
try{
    appel de la fonction susceptible de générer l'exception
} catch (Exception e){
    traiter l'exception e
}
finally{
    code exécuté après try ou catch
}
instruction suivante

Aqui, quer ocorra ou não uma exceção, o código na cláusula finally será sempre executado.

  • A classe Exception possui um método getMessage() que retorna uma mensagem detalhando o erro que ocorreu. Portanto, se quisermos exibir essa mensagem, escrevemos:
catch (Exception ex){
    System.err.println("L'erreur suivante s'est produite : "+ex.getMessage());
    ...
}//catch
  • A classe Exception possui um método toString() que devolve uma cadeia de caracteres indicando o tipo de exceção e o valor da propriedade Message. Podemos, portanto, escrever:
catch (Exception ex){
    System.err.println ("L'erreur suivante s'est produite : "+ex.toString());
    ...
}//catch

Também podemos escrever:

catch (Exception ex){
    System.err.println ("L'erreur suivante s'est produite : "+ex);
    ...
}//catch

Aqui temos uma operação string + Exception que será automaticamente convertida em string + Exception.toString() pelo compilador, a fim de concatenar duas strings.

O exemplo seguinte mostra uma exceção gerada pela utilização de um elemento de matriz inexistente:

// tables

// imports
import java.io.*;

public class tab1{
  public static void main(String[] args){
    // declaring & initializing an array
    int[] tab=new int[] {0,1,2,3};
    int i;
     // table display with for
    for (i=0; i<tab.length; i++)
      System.out.println("tab[" + i + "]=" + tab[i]);
    // generating an exception
      try{
      tab[100]=6;
    }catch (Exception e){
      System.err.println("L'erreur suivante s'est produite : " + e);
    }//try-catch
  }//hand
}//class

A execução do programa produz os seguintes resultados:

tab[0]=0
tab[1]=1
tab[2]=2
tab[3]=3
L'erreur suivante s'est produite : java.lang.ArrayIndexOutOfBoundsException

Aqui está outro exemplo em que tratamos a exceção causada pela atribuição de uma string a um número quando a string não representa um número:

// imports
import java.io.*;

public class console1{
  public static void main(String[] args){
      // creation of an input stream
    BufferedReader IN=null;
    try{
        IN=new BufferedReader(new InputStreamReader(System.in));
    }catch(Exception ex){}
     // We ask for the name
    System.out.print("Nom : ");
     // reading response
    String nom=null;
    try{
        nom=IN.readLine();
    }catch(Exception ex){}      
     // age requested
    int age=0;
    boolean ageOK=false;
    while ( ! ageOK){
       // question
      System.out.print("âge : ");
       // read-verify answer
      try{
        age=Integer.parseInt(IN.readLine());
        ageOK=true;
      }catch(Exception ex) {
        System.err.println("Age incorrect, recommencez...");
      }//try-catch
    }//while
     // final display
    System.out.println("Vous vous appelez " + nom + " et vous avez " + age + " ans");
  }//Main
}//class

Alguns resultados da execução:

dos>java console1
Nom : dupont
âge : 23
Vous vous appelez dupont et vous avez 23 ans
E:\data\serge\MSNET\c#\bases\1>console1
Nom : dupont
âge : xx
Age incorrect, recommencez...
âge : 12
Vous vous appelez dupont et vous avez 12 ans

2.7. Compilar e executar um programa Java

Compile e, em seguida, execute o seguinte programa:

// importing classes
import java.io.*;

// test class
public class coucou{
    // hand function
  public static void main(String args[]){
      // screen display
    System.out.println("coucou");
  }//hand
}//class

O ficheiro fonte que contém a classe coucou anterior deve ser denominado coucou.java:

E:\data\serge\JAVA\ESSAIS\intro1>dir
10/06/2002  08:42                  228 coucou.java

A compilação e a execução de um programa Java são feitas numa janela do DOS. Os executáveis javac.exe (compilador) e java.exe (interpretador) estão localizados no diretório bin do diretório de instalação do JDK:

E:\data\serge\JAVA\classes\paquetages\personne>dir "e:\program files\jdk14\bin\java?.exe"
07/02/2002  12:52               24 649 java.exe
07/02/2002  12:52               28 766 javac.exe

O compilador javac.exe irá analisar o ficheiro fonte .java e produzir um ficheiro .class compilado. Este ficheiro não é imediatamente executável pelo processador. Requer um interpretador Java (java.exe), conhecido como máquina virtual ou JVM (Java Virtual Machine). A partir do código intermédio no ficheiro .class, a máquina virtual gera instruções específicas para o processador da máquina na qual está a ser executada. Existem máquinas virtuais Java para diferentes tipos de sistemas operativos (Windows, Unix, Mac OS, etc.). Um ficheiro .class pode ser executado por qualquer uma destas máquinas virtuais e, portanto, em qualquer sistema operativo. Esta portabilidade entre sistemas é um dos principais pontos fortes do Java.

Vamos compilar o programa anterior:

E:\data\serge\JAVA\ESSAIS\intro1>"e:\program files\jdk14\bin\javac" coucou.java

E:\data\serge\JAVA\ESSAIS\intro1>dir
10/06/2002  08:42                  228 coucou.java
10/06/2002  08:48                  403 coucou.class

Vamos executar o ficheiro .class gerado:

E:\data\serge\JAVA\ESSAIS\intro1>"e:\program files\jdk14\bin\java" coucou
coucou

Note que no comando de execução acima, não especificámos a extensão .class para o ficheiro coucou.class a ser executado. Ela está implícita. Se o diretório bin do JDK estiver no PATH da máquina DOS, não precisamos de fornecer o caminho completo para os executáveis javac.exe e java.exe. Podemos simplesmente escrever

javac coucou.java
java coucou

2.8. Principais argumentos do programa

A função principal aceita uma matriz de cadeias de caracteres como parâmetros: String[]. Esta matriz contém os argumentos da linha de comandos utilizados para iniciar a aplicação. Assim, se iniciarmos o programa P com o comando:

        java P arg0 arg1 … argn

e se a função principal for declarada da seguinte forma:

public static void main(String[] arg);

teremos arg[0]="arg0", arg[1]="arg1" … Aqui está um exemplo:


import java.io.*;
 
public class param1{
  public static void main(String[] arg){
    int i;
    System.out.println("Nombre d'arguments="+arg.length);
    for (i=0;i<arg.length;i++)
      System.out.println("arg["+i+"]="+arg[i]);
  }
}

Os resultados são os seguintes:

dos>java param1 a b c
Nombre d'arguments=3
arg[0]=a
arg[1]=b
arg[2]=c

2.9. Passar parâmetros para uma função

Os exemplos anteriores mostraram apenas programas Java com uma única função, a função main. O exemplo seguinte demonstra como utilizar funções e como a informação é trocada entre elas. Os parâmetros de função são sempre passados por valor: ou seja, o valor do parâmetro real é copiado para o parâmetro formal correspondente.


import java.io.*;
 
public class param2{
  public static void main(String[] arg){
    String S="papa";
    changeString(S);
    System.out.println("Paramètre effectif S="+S);
    int age=20;
    changeInt(age);
    System.out.println("Paramètre effectif age="+age);
  }
  private static void changeString(String S){
    S="maman";
    System.out.println("Paramètre formel S="+S);
  }
  private static void changeInt(int a){
    a=30;
    System.out.println("Paramètre formel a="+a);
  }
}

Os resultados obtidos são os seguintes:

Paramètre formel S=maman
Paramètre effectif S=papa
Paramètre formel a=30
Paramètre effectif age=20

Os valores dos parâmetros efetivos «pai» e 20 foram copiados para os parâmetros formais S e a. Estes foram então modificados. Os parâmetros efetivos permaneceram inalterados. Note-se aqui o tipo dos parâmetros efetivos:

  • S é uma referência a um objeto, ou seja, o endereço de um objeto na memória
  • age é um inteiro

2.10. Um exemplo de cálculo de impostos

Concluiremos este capítulo com um exemplo que iremos revisitar várias vezes neste documento. Propomos escrever um programa para calcular o imposto de um contribuinte. Consideramos o caso simplificado de um contribuinte que tem apenas o seu salário para declarar:

  • calculamos o número de escalões de imposto para o trabalhador como nbParts = nbEnfants / 2 + 1 se for solteiro, e nbEnfants / 2 + 2 se for casado, sendo que nbEnfants é o número de filhos.
  • Se tiver pelo menos três filhos, recebe uma meia quota adicional.
  • Calculamos o rendimento tributável R = 0,72 × S, em que S é o salário anual
  • Calculamos o coeficiente familiar QF = R / nbParts
  • Calculamos o seu imposto I. Considere a seguinte tabela:
12 620,0
0
0
13 190
0,05
631
15 640
0,1
1.290,5
24.740
0,15
2.072,5
31 810
0,2
3.309,5
39 970
0,25
4.900
48 360
0,3
6.898,5
55 790
0,35
9.316,5
92 970
0,4
12 106
127 860
0,45
16 754,5
151 250
0,50
23 147,5
172 040
0,55
30 710
195 000
0,60
39 312
0
0,65
49 062

Cada linha tem 3 campos. Para calcular o imposto I, procuramos a primeira linha em que QF <= campo1. Por exemplo, se QF = 23.000, encontraremos a linha

    24740        0.15        2072.5

O Imposto I é então igual a 0,15*R - 2072,5*nbParts. Se QF for tal que a condição QF<=field1 nunca seja satisfeita, então são utilizados os coeficientes da última linha. Aqui:

    0                0.65        49062

o que dá o imposto I = 0,65*R - 49062*nbParts.

O programa Java correspondente é o seguinte:


import java.io.*;
 
public class impots{
 
    // ------------ hand
    public static void main(String arg[]){
 
        // data
 
        // tax bracket limits
        double Limites[]={12620, 13190, 15640, 24740, 31810, 39970, 48360,55790, 92970, 127860, 151250, 172040, 195000, 0};    
        // coefficient applied to the number of shares
        double Coeffn[]={0, 631, 1290.5, 2072.5, 3309.5, 4900, 6898.5, 9316.5,12106, 16754.5, 23147.5, 30710, 39312, 49062};
 
        // the program
 
        // keyboard input stream creation
        BufferedReader IN=null;
        try{
            IN=new BufferedReader(new InputStreamReader(System.in));
        }
        catch(Exception e){
            erreur("Création du flux d'entrée", e, 1);
        }
 
            // we recover marital status
        boolean OK=false;
        String reponse=null;
        while(! OK){
            try{
                System.out.print("Etes-vous marié(e) (O/N) ? ");
                reponse=IN.readLine();
                reponse=reponse.trim().toLowerCase();
                if (! reponse.equals("o") && !reponse.equals("n"))
                    System.out.println("Réponse incorrecte. Recommencez");
                else OK=true;
            } catch(Exception e){
                erreur("Lecture état marital",e,2);
            }
        }
        boolean Marie = reponse.equals("o");
 
            // number of children
        OK=false;
        int NbEnfants=0; 
        while(! OK){
            try{
                System.out.print("Nombre d'enfants : ");
                reponse=IN.readLine();
                try{
                    NbEnfants=Integer.parseInt(reponse);
                    if(NbEnfants>=0) OK=true; 
                        else System.err.println("Réponse incorrecte. Recommencez");
                } catch(Exception e){
                    System.err.println("Réponse incorrecte. Recommencez");
                }// try
            } catch(Exception e){
                erreur("Lecture état marital",e,2);
            }// try
        }// while
 
        // salary
        OK=false;
        long Salaire=0; 
        while(! OK){
            try{
                System.out.print("Salaire annuel : ");
                reponse=IN.readLine();
                try{
                    Salaire=Long.parseLong(reponse);
                    if(Salaire>=0)  OK=true;
                        else System.err.println("Réponse incorrecte. Recommencez");
                } catch(Exception e){
                    System.err.println("Réponse incorrecte. Recommencez");
                }// try
            } catch(Exception e){
                erreur("Lecture Salaire",e,4);
            }// try
        }// while
 
            // calculating the number of shares
        double NbParts;
        if(Marie) NbParts=(double)NbEnfants/2+2;
            else NbParts=(double)NbEnfants/2+1;
        if (NbEnfants>=3) NbParts+=0.5;
 
            // taxable income
        double Revenu;
        Revenu=0.72*Salaire;
 
            // family quotient
        double QF;
        QF=Revenu/NbParts;
 
            // search for tax bracket corresponding to QF
        int i;
        int NbTranches=Limites.length;
        Limites[NbTranches-1]=QF;
        i=0;
        while(QF>Limites[i]) i++;
            // tax
        long impots=(long)(i*0.05*Revenu-Coeffn[i]*NbParts);
 
        // the result is displayed
        System.out.println("Impôt à payer : " + impots);
    }// hand
 
    // ------------ error
    private static void erreur(String msg, Exception e, int exitCode){
        System.err.println(msg+"("+e+")");
        System.exit(exitCode);
    }// error
 
}// class

Os resultados obtidos são os seguintes:

C:\Serge\java\impots\1>java impots

Etes-vous marié(e) (O/N) ? o
Nombre d'enfants : 3
Salaire annuel : 200000
Impôt à payer : 16400

C:\Serge\java\impots\1>java impots

Etes-vous marié(e) (O/N) ? n
Nombre d'enfants : 2
Salaire annuel : 200000
Impôt à payer : 33388

C:\Serge\java\impots\1>java impots

Etes-vous marié(e) (O/N) ? w
Réponse incorrecte. Recommencez
Etes-vous marié(e) (O/N) ? q
Réponse incorrecte. Recommencez
Etes-vous marié(e) (O/N) ? o
Nombre d'enfants : q
Réponse incorrecte. Recommencez
Nombre d'enfants : 2
Salaire annuel : q
Réponse incorrecte. Recommencez
Salaire annuel : 1
Impôt à payer : 0