sábado, 8 de fevereiro de 2014

Dicas Exame Java Certification

Motivado por ter passado no exame da SCJP recentemente, publico abaixo 50 dicas práticas para o exame da versão 6. Elas foram retiradas e adaptadas do livro  Certificação SUN para Programador Java 6, de Kathy Sierra e Bert Bates, adicionadas em muitos casos de exemplos e comentários úteis.
    1. Saiba tudo sobre controle de acesso para o exame. Haverá uma boa quantidade de perguntas para as quais você deverá usar o seu conhecimento sobre o assunto. Quando vir uma questão com lógica complexa, certifique-se de olhar os modificadores de acesso primeiro;
    2. Procure questões com uma declaração de método que termine com ponto-e-vírgula em vez de chaves. Se o método estiver em uma classe – e não em uma interface -, então tanto o método como a classe devem ser marcados como abstract;
    3. Quando pensar em acesso padrão (default), pense em restrições de pacote, sem exceções. Mas quando pensar em protected, pense em pacote + filhos;
    4. Procure por classes concretas que não forneçam implementações para métodos abstratos da superclasse;
    5. Enums fora de uma classe só podem ser declarados com acesso public ou default, assim como uma classe não interna;
    6. As chamadas de métodos polimórficos se aplicam somente a métodos de instâncias;
    7. Procure classes que afirmam implementar uma interface, mas não fornecem as implementações de método corretas. A menos que a classe de implementação seja abstract,ela terá que fornecer implementações de todos os métodos definidos na interface;
    8. Lembre-se: um método static de uma classe não pode acessar um membro – método ou variável – não estático (de instância) de sua própria classe;
    9. Procure perguntas que usem números onde os booleanos seriam obrigatórios. Você pode ver uma avaliação de instrução if que use um número, como na linha a seguir:
1
2
int x = 1;
if (x) { } // Erro do compilador
    1. Tome cuidado com variáveis sombreadas e  com erros de escopo em blocos de código comoswitchestry-catches e loops;
    2. Lembre-se que quando duas referências apontem para o mesmo objeto, se uma delas for usada para alterá-lo, as duas saberão da alteração, porque ainda haverá somente um objeto. Mas sempre que forem feitas alterações em uma String, a VM atualizará a variável de referência para fazê-la apontar para um objeto diferente. Exemplo:
1
2
3
4
5
6
String x = "Pablo";
String y = x; // y e x referenciam o mesmo objeto String
 
System.out.println("y = " + y); // Imprime 'Pablo'
x = x + " Nóbrega"; // Altera somente x. Foi criado um novo objeto e a referência foi atualizada
System.out.println("y = " + y); // Continua imprimindo 'Pablo'
A saída para esse código será:
y = Pablo
y = Pablo
    1. No exame será esperado que você saiba, por exemplo, que o código abaixo produz apenas um objeto (o array atribuido a variável de referência inteiros). Nenhum objeto Integer foi realmente criado.
1
Integer[] inteiros = new Integer[5];
    1. As variáveis de referência wrapper podem ser null, portanto tome cuidado com algum código que pareça estar realizando operações seguras com primitivos, mas que lançamNullPointerException. Exemplo:
1
2
3
4
5
6
7
8
9
10
class TesteTempo {
   static Long tempo;
 
   static void calculaTempo(int parametro) {
      System.out.println(parametro + 5);
   }
   public static void main(String[] args) {
      calculaTempo(tempo); // NullPointerException. 'tempo' não foi inicializada
   }
}
    1. Nenhuma das classes wrapper pode ser ampliada de uma para outra. Por exemplo: Bytenão se amplia para ShortShort não se amplia para Long, etc. O código abaixo não vai funcionar:
1
2
3
4
5
6
7
8
9
10
class TesteAmpliacao{
   void ampliar(Long parametro) {
      System.out.println(parametro);
   }
 
   public static void main(String[] args) {
      TesteAmpliacao teste = new TesteAmpliacao();
      teste.ampliar(new Integer(10)); // impossível ampliar de Integer para Long
   }
}
    1. Tenha em mente que uma vez que o coletor de lixo entre em ação, não há garantia de que todos os objetos não utilizados serão realmente removidos da memória;
    2. Fique atento! A parte do exame referente aos operadores e atribuições normalmente é aquela em que os candidatos conseguem menos pontos. Por isso, muita atenção nas questões que envolvem esses assuntos;
    3. Você deve compreender de forma clara como a concatenação de Strings funciona, principalmente dentro de uma instrução print. Veja o exemplo abaixo:
1
2
3
int b = 2;
System.out.println("" + b + 3); // Imprime '23'
System.out.println(b+3); // Imprime 5
    1. Procure questões que usem operadores de incremento e decremento em variáveis final. Obviamente que qualquer tentativa de utilizá-los nessa situação resulta em uma exceção.
    2. Os operadores && e || só funcionam com operadores booleanos. Você pode encontrar questões que usem inteiros com esses operadores (dando a impressão que estamos usando o operador bit a bit &). Exemplo:
1
if (5 && 6) // Erro de compilação.
    1. Retirando-se uma saída forçada (breakreturnSystem.exit(), etc), as avaliações da expressão de iteração e, posteriormente, da expressão condicional, são sempre as duas últimas coisas a ocorrerem em um loop for.
    2. Procure códigos que invoquem métodos que declarem uma exceção, mas o método que estiver chamando não manipule ou declare a exceção verificada. Exemplo abaixo:
1
2
3
4
5
6
7
void aumenta() { // falta método manipular ou declarar a exceção que deveria ser lançada por realizaCalculo()
   realizaCalculo();
}
 
void realizaCalculo() { // falta método lançar com throws
   throw new IOException();
}
    1. Se você se deparar com a palavra expressão em uma pergunta sobre assertivas e ela não especificar se está se referindo à primeira expressão (teste booleado) ou à segunda (o valor a ser exibido no rastreamento da pilha), então assuma que sempre está se referindo ao teste booleano.
    2. Os arrays têm um atributo (não um método) chamado length. É possível que você encontre alguma pergunta que troque o atributo pelo método length(), da classe String, ou vice-versa. Exemplo:
1
2
3
String teste = "teste";
System.out.println(teste.length); // Erro de compilação
}
    1. Criar diretórios em Java é diferente de criar arquivos. Ao se construir um Reader ou umWriter, automaticamente um arquivo é criado, caso não exista. Porém o mesmo não ocorre com diretórios, ou seja, o diretório não será criado caso estejamos tentando escrever em um novo arquivo daquele diretório que você deseja que exista. Veja o exemplo abaixo e observe que a linha que cria o diretório está comentada:
1
2
3
4
File diretorio = new File("novoDir");
// diretorio.mkdir(); // Criação de diretório comentada
File arquivo = new File(diretorio, "meuArquivo.txt");
arquivo.createNewFile(); // Exceção por conta de o diretório não existir.
    1. Você deverá saber quais variáveis são restauradas com os valores apropriados e quais não são, quando um objeto é deserializado. Veja um exemplo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.io.*;
class SuperClasseNaoSerializada {
   public static void main(String[] args) {
      Leao leao = new Leao(120, "Juba");
      System.out.println("antes de deserializar: " + leao.nome + " " + leao.tamanho);
      try {
         FileOuputStream fos = new FileOutputStream("arquivo.txt");
         ObjectOutputStream oos = new ObjectOutputStream(fos);
         oos.writeObject(leao); oos.close();
      } catch (Exception ex) { e.printStackTrace(); }
 
      try {
         FileInputStream fis = new FileInputStream("arquivo.txt");
         ObjectInputStream ois = new ObjectInputStream (fis);
         leao = (Leao) ois.readObject(); ois.close();
      } catch (Exception ex) { e.printStackTrace(); }
 
      System.out.println("depois de deserializar: " + leao.nome + " " + leao.tamanho);
   }
 
}
class Leao extends Animal implements Serializable {
   String nome;
   Leao(int t, String n) {
      tamanho = t; // herdado de Animal
      nome = n; // não herdado
   }
}
class Animal {
   int tamanho = 105;
}
A saída para esse código será:
antes de serializar: Juba 120
depois de deserializar: Juba 105
    1. Atenção: a contagem dos meses em um Calendar começa em 0 (zero);
    2. Os objetos DateFormat NumberFormat podem ter os seus locais definidos somente no momento da instanciação. Cuidado com códigos que tentam modificar o local de uma instância existente. Exemplo:
1
2
3
4
5
6
class TestaLocal {
   public static void main(String[] args) {
      DateFormat df = DateFormate.getInstance();
      df.setLocale(new Locale("it", "IT"));
   }
}
    1. Metacaracteres e Strings não trabalham bem juntos. Portanto, tome cuidado com código como o abaixo, pois ele não compila:
1
2
3
4
5
6
class TestaPattern {
   public static void main(String[] args) {
      String patternInvalido= "\d"; // Pattern inválido. Não compila.
      Pattern p = Pattern.compile(patternInvalido);
   }
}
    1. Quando encontrar uma sobrescrição de equals(), hashCode() e toString() verifique se não houve uma mudança de visibilidade, pois o método deve ser public. Além disso, observe se não está sendo feita uma sobrecarga, ao invés de sobrescrita. O primeiro código abaixo, por exemplo, é inválido e o segundo é uma sobrecarga válida:
1
2
3
4
5
class ErroEquals {
   boolean equals(Object o) { // visibilidade default: não compila
      return false;
   }
}
1
2
3
4
5
class Bola {
   boolean equals(Bola bola) { // sobrecarga válida
      return true;
   }
}
    1. Na prova, tenha cuidado com a interpretação de certas questões. Não confunda, por exemplo, o uso apropriado ou correto do código de hashing, com a validade ou eficiência da solução implementada;
    2. Cuidado para não confundir as classes Collections CollectionCollection é uma interface com as declarações de métodos comuns a várias coleções e Collections é uma classe com métodos estáticos utilitários;
    3. É importante reconhecer o que é interface e o que é classe concreta quando se trata de coleções. O código abaixo não compila porque Map é uma interface:
1
2
3
4
5
class TestaMap {
   public static void main(String[] args) {
      Map<String, String> mapa  = new Map<String, String>();
   }
}
    1. Entenda o funcionamento das classes Comparator Comparable, pois elas são bastante parecidas. Analise principalmente a forma como elas trabalham e o método que utilizam para efetuar a comparação;
    2. Procure por questões no exame que tentem classificar um array de primitivos usando umComparator, o que é inválido;
    3. Quando houver classificação de um array ou de uma coleção, os elementos contidos nesses objetos devem ser mutuamente comparáveis. Você não pode ter um Object[], colocar dentro dele um objeto Cachorro e um objeto Gato e depois efetuar uma classificação;
    4. Os erros mais comuns em busca e classificação são:
      • Procurar em um array ou coleção que não tenha sido ordenado;
      • Usar um Comparator ou só na classificação ou só na busca;
    5. Quando converter arrays em Collections, lembre-se que a atualização em um dos objetos refletirá no outro. O mesmo não vale para quando convertemosCollections em arrays. Observe o código abaixo:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class TestaColecoes {
   public static void main(String[] args) {
      String[] nomes = {"Pablo", "Nóbrega"};
      List nomesList = Arrays.asList(nomes);
      nomesList.set(1, " - Autor do Post");
      // imprime 'Pablo - Autor do Post' ao invés de 'Pablo Nóbrega'
      for (String s : nomes) {
         System.out.print(s);
      }
      System.out.println();
      List escritoresList = new ArrayList();
      escritoresList.add("Camões");
      escritoresList.add(" - Drummond");
      String[] escritoresArray = new String[escritoresList.size()];
      escritoresList.toArray(escritoresArray);
      escritoresArray[1] = " - Pessoa";
 
      // continua imprimindo 'Camões - Drummond'
      for (int i = 0; i < escritoresList.size(); i++) {
         System.out.print(escritoresList.get(i));
      }
   }
}
A saída para esse código será:
Pablo – Autor do Post
Camões – Drummond
    1. Certifique-se de que você entende bem a diferença entre “compilação falha”“compila sem erros”“compila sem avisos” e “compila com avisos”. Em genéricos e na mistura de código com e sem tipos específicos, os avisos podem fazer diferença;
    2. Ao declarar um conjunto não-genérico, o método get() irá retornar Object, portanto é necessário efetuar cast explícito. O código abaixo, por exemplo, não compila:
1
2
3
4
5
6
7
8
class TestaGenericos {
   public static void main(String[] args) {
      List teste = new ArrayList();
      teste.add(43);
      int x = teste.get(0);
      System.out.println(x);
   }
}
    1. Em genéricos, os coringas só podem ser usados para declarações de referências, ou seja, enquanto uma referência pode ser abstrata e polimórfica, o próprio objeto criado deve ser de um tipo específico. Você precisa definir o tipo quando cria o objeto usando new. O exemplo abaixo não é válido:
1
2
3
4
5
class TestaGenericos {
   public static void main(String[] args) {
      List<?> animais =   new ArrayList<? extends Animal>();
   }
}
    1. Classes internas locais de métodos só podem ser instanciadas dentro do método onde foram criadas e acessam normalmente os membros privados da classe externa, entretanto não poderão usar as variáveis locais do método onde elas estiverem. A única exceção é no caso de as variáveis serem final. Veja o código a seguir:
1
2
3
4
5
6
7
8
9
10
class TestaClasseLocalMetodo {
   public void fazAlgo() {
      int x = 1;
      class ClasseInvalida {
         public void naoRoda() {
            System.out.println("x = " + x); // não compila. 'x' não está visível. Solução: declarar 'x' como final
         }
      }
   }
}
    1. Em classes internas anônimas, tome cuidado com o ponto-e-vírgula de fechamento da definição da classe. Caso ele não esteja presente, o código não compila. Observe o exemplo:
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Animal {
   public void pula() {
      System.out.println("pulou");
   }
}
 
class Cachorro {
   Animal a = new Animal() {
      public void pula() {
         System.out.println("pulo 2");
      }
   } // Não compila. Falta ponto-e-vírgula
}
    1. Saiba identificar uma classe interna anônima que, ao invés de sobrescrever um método da superclasse, define um método novo.
    2. Cuidado com instanciação de interface ou classes abstratas. O primeiro código a seguir não roda, mas o segundo sim.
1
Runnable r = new Runnable(); // Não compila. Tentando instanciar uma interface.
1
2
3
Runnable r = new Runnable() { // Compila. Classe anônima.
   public void run() { }
};
    1. É muito importante você saber a maneira como uma thread funciona. O exame tem perguntas a respeito de “comportamento garantido” em concorrência que normalmente caem;
    2. Atenção com métodos run sobrecarregados. A classe Thread espera um método run() sem argumentos e o executará ao iniciá-la.
    3. A classe Thread implementa Runnable, portanto é perfeitamente fazer o seguinte:
1
2
Thread t = new Thread(new Thread());
t.start();
  1. Lembre-se que uma vez uma thread tenha sido iniciada, ela jamais pode ser iniciada novamente;
  2. Ao terminar a execução do método sleep(), não significa que a thread retornará ao estado de execução, pois ela fica com o status de executável. O método sleep(), portanto, não deve ser encarado como um timer preciso;
  3. Entenda a estrutura dos comandos javac java. No teste sempre caem perguntas simples do assunto.

Nenhum comentário:

Postar um comentário