Pular para o conteúdo principal

Guia da certificação Java SE 8 Programmer l - Parte 3: Operadores, decisões e loops

Seja bem-vindo a série de postagens sobre a certificação Java. Como funciona, o que fazer para comprar, marcar o dia da prova e o principal, o que estudar.

Para ver o índice da série e as datas das publicações, acesse este link


Parte 3 – Operadores, decisões e loops

Neste post, vamos dar mais um passo nos detalhes da linguagem quanto aos operadores, decisões e loops.

Objetivos do exame

  • Precedência com uso de parenteses
  • If, if/else e operadores ternários
  • Usando o switch
  • Usando while
  • Loops for
  • Do/while loops
  • Break e continue

Operadores Java

Para o exame, será preciso conhecer os operadores Java e como ela é resolvida. Nem sempre a operação será resolvida da esquerda para a direita. Ex:
int y = 8;
int x = 2 - 5 + 3 * 4 - --y;

No exemplo acima, é feita a subtração de uma unidade de y, após é feita a multiplicação e depois resolve da esquerda para a direita. O resultado a operação acima é 2.

Os operadores que aparecerão na prova são: (+), (-), (*), (/) e resto da divisão (%). Os operadores (*, / e %) tem precedência sobre os operadores (+ e -). Para dar preferência a um operador de menos precedência, utilizamos os parenteses.
int x = (2 - 5 + 3) * 4 - --y;

Agora o resultado é -7

O operador + e += pode ser aplicado também a String, resultando na concatenação. O operador resto (%) é exatamente o resto da divisão entre dois números.

System.out.println(21 / 4); //resultado = 5
System.out.println(21 % 4); //resultado = 1

Você deve lembrar que se fizer uma operação com dois tipos de números diferentes, o tipo com menos tamanho será convertido para o maior tipo. Ex:

int y = 8;
long x = 10;
int w = y + x; //erro de compilação

Se um dos valores for de ponto flutuante, o valor inteiro será promovido automaticamente para ponto flutuante. No caso dos dois valores forem de ponto flutuante, o que vale é o double.
double a = 2.2;
float b = 3.3f;
float c = a * b;//erro de compilação

Cuidado com algumas pegadinhas na prova.

Inversão de valores

int x = -14;
System.out.println(x); //print -14
x = -x;
System.out.println(x); //print 14

Qualquer valor abaixo de int que tiver uma operação matemática executada, será transformado em int.
short a = 1;
short b = 2;
short c = a + b; //erro de compilação

Porem se utilizarmos operadores de incremento e decremento (-- e ++) para atribuir a variável, é feito o cast automaticamente.(++) e (--) tem preferência aos operadores binários. Dependendo da localização do operador, o resultado da expressão pode ser diferente. Ele pode ser colocado antes ou depois da variável.

short a = 1;
short b = ++a; //ok
int x = ++b + a--; //válido
System.out.println(x); //Resultado é 5

Cast de dados primitivos

Quanto um valor literal não cabe no tipo de variável declarada, ainda é possível atribuir o valor fazendo um cast. Com o cast e possível associar um valor de ponto flutuante em um int por exemplo. Um valor maior a um short. Porém cuidado com este tipo de conversão, pois poderá haver perda de consistência. Cuidado com atribuição dentro de atribuições.
x = 1
y = (x = 3) //ok, y = 3

Operadores relacionais

Sempre retornam um valor booleano. O operador instanceof não é cobrado no exame menor que (<), menor ou igual(<=), maior que (>) e maior ou igual (>=).

Operadores lógicos

Temos três operadores lógicos: e ( & ), or inclusivo ( | ) e or exclusivo( ^ )

Operadores de curto circuito

Para este tipo de operador, a diferença é que se a expressão da direita retornar true, o restante da expressão não é avaliada. Um exemplo clássico deste uso é a verificação de uma variável.
if (a != null && a.equals("a")){
}
Caso a variável a for null, então a segunda expressão nunca será executada, evitando uma exceção. Para o exame, é possível ter uma pergunta que incrementa uma variável em uma expressão. Ex:
String a = "a";
int b = 6;
if (a != null && b++ == 7){
    System.out.println(a);
}
System.out.println(b);
O que acontece se executarmos o código acima? A variável a não será impressa, porém o valor da variável b será 7 após o if. Caso a variável a fosse null, então o valor de b seria 6. A variável b não é 7 no momento do if, pois está utilizando um operador de incremento posterior.

Comparação de igualdade (==)

É possível comparar objetos, números primitivos e valores booleanos.
System.out.println(500 == 5_0_0.0_0_0__0); //true
O exemplo acima compila e retorna true, pois o Java consegue converter o valor da esquerda para double. Para comparação de objetos, o operador de igualdade é aplicado às referências aos objetos, não os objetos para os quais eles apontam. Duas referências são iguais se e somente se apontarem para o mesmo objeto, ou ambos apontam para null.

Relação completa dos operadores Java:
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/opsummary.html

Java Statements


O controle de fluxo de um código Java é feito pelos statements, conhecidos como: if/else, if/then/else, operador ternário, switch, while, do-while e for.

If sempre deve retornar um booleano, porém podemos ter atribuições de variáveis ou incremento de valores dentro da expressão. Então muito cuidado ao analisar questões como esta:
int x = 1;
if (++x > 1)
System.out.println("x is greater than one!");
No código acima, teremos a impressão da mensagem pois a variável x foi incrementada antes da verificação da condição. E também como tínhamos apenas uma instrução dentro do if, não precisamos das chaves. Na prova pode ocorrer que o código não esteja identado e te induza ao erro.
int x = 1;
if (x++ > 1)
    System.out.println("x is greater than one!");
    System.out.println("x is " + x);
No código acima, você poderá ser questionado sobre a saída da execução. Olhe com cuidado, pois o if não possui abertura de chaves, então somente a primeira linha após o if seria executada caso a expressão retornasse true. O que não é o caso. Como o x é incrementado depois, ele não será maior que 2. Porém o segundo print não esta dentro do if e a saída da execução será 2.
Para o if/else, a regra é a mesma. Caso tenha apenas uma instrução apos o if, não é necessário as chaves.
int x = 1;
if (x++ > 1)
    System.out.println("x is greater than one!");
else
    System.out.println("x is not greater than one");
    System.out.println("x is " + x);
Neste outro código, o último System.out sempre será executado independente do resultado da expressão. Uma vez executado o código abaixo da expressão verdadeira, os demais ifs serão ignorados. Então a ordem dos ifs é importante.
int hourOfDay = 10;
if(hourOfDay < 15) {
    System.out.println("Good Afternoon");
} else if(hourOfDay < 11) {
    System.out.println("Good Morning"); // nunca será executado
} else {
    System.out.println("Good Evening");
}
Sempre verifique se a expressão esta verificando uma condição booleana. O código abaixo não compila, pois esta sendo feita uma atribuição dentro do if.
int age = 18;
if(age = 18)
    System.out.println("Major!"); //não compila

Operador ternário

Um dos operadores que mais pode causar confusão no momento da prova. O operador é relativamente simples, porém se ele aparecer de forma encadeada, ai fica difícil e demorado para verificar o resultado. Pense no operador ternário como um if/then/else.
if ? then : else
int x = 1;
int y = x > 1 ? 10 : 20;
O código acima seria o mesmo que:
int x = 1;
int y;
if (x > 1) {
    y = 10;
} else {
    y = 20;
}
Agora um exemplo que talvez você encontre na prova:
int x = 1;
int y = x >= 1 ? x == 1 ? 2 : 3 : 20;
Se o x for maior ou igual a 1, então ele avalia outra expressão. Se o x for igual a 1, então retorna 2, senão retorna 3;

Switch

A partir do Java 7, a String também foi incorporada no switch. Os tipos de dados suportados são:
■ int e Integer
■ byte e Byte
■ short e Short
■ char e Character
■ int e Integer
■ String
■ enum

Somente os tipos acima são permitidos. Não tente utilizar um double ou long por exemplo. Você terá um erro de compilação. No caso do switch, sempre devemos utilizar o break para que o fluxo siga para fora do switch, pois caso não utilizar, o Java continua verificando as condições.
int age = 1;
switch(age) {
    default:
        System.out.println("default");
    case 2:
        System.out.println("two years old");
    case 3:
        System.out.println("three years old");
}
O que acontece ao executar o bloco de código acima? Por mais que nenhuma condição seja atendida explicitamente, foi utilizado o default que será executado quando nenhuma condição for verdadeira. E como não foi utilizada a instrução break, todas as condições abaixo serão executadas.

Todo valor da expressão case deve ser uma constante e do mesmo tipo da expressão switch. Pode ser um literal, um enum, uma variável final.
String var = "test1";
final String var1 = "test2";
switch (args[0]) {
    case "test":
        System.out.println("test");
    case var: //erro de compilação
        System.out.println("test1");
    case var1:
        System.out.println("test2");
}

While

Um while é parecido com o if, porém a instrução dentro do while é executada enquanto a condição for verdadeira.
int x = 0;
while (x++ < 10)
    System.out.println(x);
Assim como o if, não é obrigatório ter a abertura de chaves. Neste caso o print será executado 10 vezes e então continuar o fluxo. Caso o x não fosse incrementado dentro do while, então teríamos um loop infinito. Seria a mesma coisa que fazer um while (true).

Do while

A diferença para o while é que o do/while será executado pelo menos uma vez antes de verificar a expressão booleana.

Comando for

O comando for é um pouco mais complexo que o while e o do/while. Ele tem duas formas. A primeira chamada de forma básica existente desde o Java 1. A partir do Java 5, temos o for-each. O for básico possui três blocos (inicialização; expressão booleana; incremento) e todos eles são opcionais. Temos apenas como obrigação separar com ponto e virgula.
for (; ; )
    System.out.println("infinite loop!");
A instrução acima é válida e faz um loop infinito devido ao não controle do fluxo. Podemos utilizar múltiplas instruções nos blocos do for. Inicializando varias variáveis, verificando expressões booleanas e incrementando algumas variáveis.
for (int x = 0, y = 0; x <= 10 && y <= 10 ; x++, y++){
    System.out.println(x);
    System.out.println(y);
}
No exemplo acima, estamos inicializando duas variáveis, comparando os valores e incrementado. Esse tipo de questão é bem comum na prova, então pratique bastante. Não é possível redeclarar uma variável dentro do for. Ex:
public static void main(String[] args) {
    int y = 1;
    for (int x = 0, y = 0; x <= 10 && y <= 10 ; x++, y++){ //erro
}
Para o código acima compilar, basta retirar a declaração as variáveis de dentro do for e passar para fora do for. Outro detalhe é que não é possível declarar tipos diferentes no bloco de inicialização. E lembre-se, caso a variável tenha sido declarada no bloco de inicialização do for, ela é de escopo local do for e não pode ser utilizada fora dele.

For-each

O for-each foi introduzido na versão 5 do Java e para o exame é preciso reconhecer uma iteração utilizando o List e ArrayList:
List<String> names = Arrays.asList("Sara", "Lisa", "Andrew");
for (String name : names) {
    System.out.println(name);
}
O for é divido em dois blocos. O primeiro é a declaração do objeto que esta dentro da coleção. No caso, para uma lista de Strings é declarado apenas o objeto String. Separamos o bloco de inicialização do bloco da lista a ser iterada por dois pontos.

Controle de fluxo avançado

Loops podem conter outros loops, então as coisas podem ficar um pouco mais complicadas no exame. O que é bem comum.
int[][] matrix = {{4, 3, 2, 1}, {3, 4, 5, 6, 3}, {2, 3, 4, 5, 6, 7}};
for (int[] ints : matrix) {
    for (int i = 0; i < ints.length; i++) {
        System.out.print(ints[i] + "\t");
    }
    System.out.println();
}
Dentro destes loops ainda é possível que tenha uma lógica e você deve acertar o resultado de saída.
int x = 0, y = 2;
while (x < 5) {
    x++;
    do {
        y++;
    } while (y < 10);
}
System.out.println(x);
System.out.println(y);
Qual o resultado de x e y apos a execução do código acima?

break e continue

A diferença entre o break e o continue é que o break irá terminal o fluxo do loop atual e continuará para fora do loop e o continue interromperá o loop atual e retornará para a validação booleana se deve continuar com o loop. Existem ainda os labels, porém não são cobrados na prova.

Para saber mais:
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/branch.html

Conclusão

O tópico de controle de fluxo e repetição sem dúvida é um dos mais importantes do exame, pois a maioria dos exemplos de código terão algum if ou loop. Ocasionalmente a pergunta pode ser de um determinado assunto, porém a resposta pode ser um erro de compilação devido ao if, for ou loop que não compila.

Bons estudos e até a próxima!









Comentários

Postagens mais visitadas deste blog

Java Records

  Java Records Imutável, Simples e limpa Esta funcionalidade da linguagem apareceu pela primeira vez na versão 14 como experimental e assim continuou até a versão 15 . Agora liberada de forma definitiva no Java 16 . O objetivo é ser possível ter classes que atuam como portadores transparentes de dados imutáveis. Os registros podem ser considerados tuplas nominais. Ou seja, após criado, um record não pode mais ser alterado. Records oferece uma uma sintaxe compacta para declarar classes que são portadores transparentes para dados imutáveis superficiais visando reduzir significamente o detalhamento dessas classes e irá melhorar a capacidade de leitura e manutenção do código. Vamos seguir um exemplo de uma classe chamada Pessoa . O primeiro exemplo vamos utilizar o modo tradicional. public class Pessoa { private String nome; private int idade; public Pessoa (String nome, int idade) { super (); this .nome = nome; this .idade = idade; } public String getNo

Java 8 ao 18: Mudanças mais importantes na plataforma Java

    Vamos rever muitas das mudanças mais importantes na plataforma Java que aconteceram entre a versão 8 (2014) e 18 (2022)   O Java 8 foi lançado em março de 2014 e o Java 18 em março de 2022. São 8 anos de progresso, 203 JEPs (JDK Enhancement Proposals ), entre essas duas versões. Neste post, revisaremos as mudanças mais importantes e discutiremos os benefícios e desafios da adoção de versões mais recentes do JDK para novos aplicativos e para os mais antigos compilados com versões mais antigas. Desde a versão 9, o Java tem novos recursos a cada 6 meses e é muito difícil acompanhar essas novas mudanças. A maioria das informações na internet descreve as mudanças entre as duas últimas versões do Java. No entanto, se você estiver em uma situação semelhante à minha, não está usando uma das versões mais recentes do Java, mas uma das várias versões anteriores (Geralmente 8 ou 11 que são as versões de suporte estendido). Então é útil saber quais novos recursos foram adicionados d

O suporte de longo prazo e o que o LTS significa para o ecossistema Java

A arte do suporte de longo prazo e o que o LTS significa para o ecossistema Java Aqui está o que o Java 17 tem em comum com o Java 11 e o Java 8. Em junho de 2018, há pouco mais de três anos, a Oracle e outros participantes do ecossistema Java anunciaram uma mudança no modelo de cadência de lançamento para Java SE. Em vez de ter um lançamento principal planejado a cada dois ou quatro anos (que geralmente se torna de três a quatro anos), um novo modelo de lançamento de recursos de seis meses seria usado: a cada três anos, um lançamento seria designado como Long-Term Support (LTS) e receba apenas atualizações trimestrais de segurança, estabilidade e desempenho. Esse padrão foi emprestado descaradamente do modelo de lançamento do Mozilla Firefox, mas o ajustou para ficar mais alinhado com os requisitos de uma plataforma de desenvolvimento. A primeira versão do Java lançada sob esse modelo foi o Java SE 11. O lançamento do Java SE 17, o segundo lançamento do LTS sob o novo