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.
- 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;
- 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;
- 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;
- Procure por classes concretas que não forneçam implementações para métodos abstratos da superclasse;
- Enums fora de uma classe só podem ser declarados com acesso public ou default, assim como uma classe não interna;
- As chamadas de métodos polimórficos se aplicam somente a métodos de instâncias;
- 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;
- 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;
- 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 |
- Tome cuidado com variáveis sombreadas e com erros de escopo em blocos de código comoswitches, try-catches e loops;
- 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 StringSystem.out.println("y = " + y); // Imprime 'Pablo'x = x + " Nóbrega"; // Altera somente x. Foi criado um novo objeto e a referência foi atualizadaSystem.out.println("y = " + y); // Continua imprimindo 'Pablo' |
A saída para esse código será:
y = Pablo
y = Pablo
y = Pablo
y = Pablo
- 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]; |
- 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 }} |
- Nenhuma das classes wrapper pode ser ampliada de uma para outra. Por exemplo: Bytenão se amplia para Short, Short 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 }} |
- 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;
- 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;
- 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 |
- 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.
- 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. |
- Retirando-se uma saída forçada (break, return, System.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.
- 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();} |
- 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.
- 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} |
- 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 comentadaFile arquivo = new File(diretorio, "meuArquivo.txt");arquivo.createNewFile(); // Exceção por conta de o diretório não existir. |
- 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
antes de serializar: Juba 120
depois de deserializar: Juba 105
- Atenção: a contagem dos meses em um Calendar começa em 0 (zero);
- Os objetos DateFormat e 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")); }} |
- 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); }} |
- 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; }} |
- 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;
- Cuidado para não confundir as classes Collections e Collection. Collection é 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;
- É 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>(); }} |
- Entenda o funcionamento das classes Comparator e 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;
- Procure por questões no exame que tentem classificar um array de primitivos usando umComparator, o que é inválido;
- 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;
- 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;
- 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
Pablo – Autor do Post
Camões – Drummond
- 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;
- 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); }} |
- 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>(); }} |
- 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 } } }} |
- 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} |
- Saiba identificar uma classe interna anônima que, ao invés de sobrescrever um método da superclasse, define um método novo.
- 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() { }}; |
- É 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;
- Atenção com métodos run sobrecarregados. A classe Thread espera um método run() sem argumentos e o executará ao iniciá-la.
- A classe Thread implementa Runnable, portanto é perfeitamente fazer o seguinte:
1
2
| Thread t = new Thread(new Thread());t.start(); |
- Lembre-se que uma vez uma thread tenha sido iniciada, ela jamais pode ser iniciada novamente;
- 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;
- Entenda a estrutura dos comandos javac e java. No teste sempre caem perguntas simples do assunto.
Nenhum comentário:
Postar um comentário