[Tens de ter uma conta e sessão iniciada para poderes visualizar esta imagem]


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. Não fosse o controle de fluxo, um programa poderia executar apenas uma única seqüência de tarefas, perdendo completamente uma das características mais interessantes da programação: a dinâmica.

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
























Comando  Palavras-chave 
Tomada de decisões  if-elseswitch-case 
Laços ou repetições  forwhiledo-while 
Apontamento e tratamento de excessões  try-catch-finallythrow 
outros  breakcontinuelabel:return 

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 outro comando é executado; se for false, a excecussão do programa segue adiante. A sintaxe para esse comando é

if ([condição])
[comando] // Executado se a condição for true
Uma variação do comando if-else permite escolher alternadamente entre dois outros 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 outros dois comandos é executado, do contrário, o segundo.

Código:


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.print("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 mensagem de aviso
    if(x >= 0)
      System.out.println("raiz quadrada de x e " + sqrt(x));
    else
      System.out.println("x e negativo");
  }

}

E se toda uma lista de comandos deve ser executada seletivamente? A esta altura, convém dizer que há uma maneira bastante conveniente de agrupar longas seqüências de comandos formando uma unidade lógica. Trata-se da noção de bloco de comandos. Este consiste basicamente de uma lista de comandos delimitados por chaves { }. Para efeitos de programação, um bloco de comandos é interpretado como se fosse um único comando. Eis alguns exemplos de blocos de comandos:

Código:

{

System.out.print("x = "); 
System.out.println(x); 
}

{ temp = y; y = x; x = temp; }

Desse modo, podemos 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:

Código:

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 da equação ax^2+bx+c = 0");
      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;
      b = 0;
      c = 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.");
  }
}

if-then-else compacto
O if-else compacto 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 [condição condicional] deve resultar em true ou false, [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.

Para melhor ilustrar o if-else compacto, consideremos o seguinte comando:

y = x < 1 ? x*x : 2-x;
Este é 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 esteja presente 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), podeser codificado como

y = ( x<0 ? -x : x ) * Math.sin(x); // aqui os parenteses 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.

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 através do aninhamento ou acoplamento de vários comandos if-else, do seguinte modo:

Código:


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. Vejamos um exemplo:

Código:


import java.io.StreamTokenizer;

public class sqrt {

  public static void main(String args[]) {
    double x, y;
    StreamTokenizer inp = new StreamTokenizer(System.in);
    /* Desejamos 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);
  }
}

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:

Código:


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

A [expressão] pode ser qualquer expressão válida. Esta é 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). Se o valor for diferente de todas essas constantes, então o comando presente sob o rótulo default: é executado (e todos os demais são saltados), caso este esteja presente. Por exemplo:

Código:


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);
        break;
      default:
        System.out.println("default: op nao esta no limite 1..3");
    }
  }
}

Laço de iteração enquanto/faça

Freqüentemente, desejamos que uma tarefa seja executada repetidamente por um programa enquanto uma dada condição seja verdadeira. Isso é possível 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 subjacente é executado; se a expressão for false, então o comando é saltado e a execução prossegue adiante. A diferença é que após executar o comando subjacente, a expressão condicional é novamente 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 a expressão é avaliada antes de uma possível execução do comando subjacente, o que significa que esse comando pode jamais ser executado.

Código:


while([condição])
  [comando subjacente]
Por exemplo:
// Achar o raiz quadrada de 2 pelo metodo 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 < (raiz de 2) < 2
    while( (b-a) > erro ) {
      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);
 }
}

Uma das observações importantes é sempre certificar que não ocorra o laço infinito (um laço que nunca para, pois a condição sempre é verdadeira). Caso tenha alguma chance de entrar no laço infinito, coloque um contador ou user o laço for.

Laço de iteração faça/enquanto

Um outro tipo de laço de repetição, similar ao enquanto/faça, é o laço faça/enquanto. Este é introduzido por um par de comandos do/while 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. Vejamos o seguinte exemplo:

Código:

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--> Opcao invalida");
      }
    } while(op != 'f');
    System.out.println("\nAte logo.");
  }
}

Laço de iteração com contagem

Em certas situações, precisamos de laços de repetições nos quais alguma variável é usada para contar o número de iterações. Para essa finalidade, temos o laço for. Este é o tipo de laço mais geral e mais complicado disponível na linguagem Java. Sua sintaxe é a seguinte:

for ([expressão 1]; [condição]; [expressão 2])
[comando]
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:

Código:

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

Isto que dizer que o laço for avalia inicialmente a expressão de inicialização. Em seguida, avalia a expressão condicional. Se o valor desta for true, então o comando é executado, a segunda expressão é avaliada em seguida, e finalmente o laço volta a avaliar novamente 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 manter variáveis de contagem, conforme ilustra o seguinte exemplo:

for (i=0; 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-1. O operador ++ foi utilizado na ultima expressão do laço for para somar um ao valor da variável de contagem. Caso necessitássemos de um incremento (ou decremento) maior do que um, poderíamos usar os operadores += ou -=. Por exemplo, para imprimir todos os números pares entre de 0 até 10, poderíamos fazer:

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++);

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 ocorrencia de alguma condição específica, antes da chegada do final natural do laço.

Exemplo:

Código:


// Achar i tal que v[i] é negativo
for(i=0; i<n;i++)
  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:

Código:


// Notificando a existencia 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 pular direto para final do laço, mas em vez de interromper o laço como no break, ele continua executando o próximo passo do laço. Não vamos ficar estudando o uso de continue por ser puco usual na programação estruturada.

O comando "goto" em Java é feito pelo break ou continue com rótulo. Isto é útil para interromper de uma vez só, a execução de um laço multiplo ou pular para um determinado ponto do programa. O uso de saltos deste tipo deve ser evitado na programação estruturada, exceto quando trata de alguma otimização muito importante. Caso contrário, nosso programas pode tornar totalmente ilegível. Nós vamos ver um exemplo, mas lembre-se que se necessitar de um "goto", está faltando a estruturação do programa. Nós estudamos essas coisas para poder entender o programa já feito.

exemplo:

Código:


// calculando a raiz da soma dos quadrados de A[i][j]
soma = 0;
for(i=0;i<m;i++)
 for(j=0;j<n;j++)
  if(A[i][j]<0) {
      System.out.println("Erro. existe elemento negativo.");
      break fim;
  }
  esle
      soma += Math.sqrt(A[i][j]);

fim:

Código:

System.out.println("soma = " + soma);

o nome colocado na localização é denominado de rórotulo e deve ser declarado como label.

Exercícios

Implemente os exemplos desta seção. Eventualmente alguns exemplos necessitarão ser adequadamente completados para poderem executar.