Erros de programadores Java iniciantes. Parte 1
Autor: A.Grasoff™ Link para a fonte: Erros de programadores Java iniciantes
9. Chamando métodos de classe não estáticos do método main()
O ponto de entrada de qualquer programa Java deve ser um método estáticomain
:
public static void main(String[] args) {
...
}
Como esse método é estático, você não pode chamar métodos de classe não estáticos a partir dele. Os alunos muitas vezes esquecem disso e tentam chamar métodos sem criar uma instância da classe. Esse erro geralmente é cometido logo no início do treinamento, quando os alunos escrevem pequenos programas. Exemplo errado:
public class DivTest {
boolean divisible(int x, int y) {
return (x % y == 0);
}
public static void main(String[] args) {
int v1 = 14;
int v2 = 9;
// на следующие строки компилятор выдаст ошибку
if (divisible(v1, v2)) {
System.out.println(v1 + " is a multiple of " + v2);
} else {
System.out.println(v2 + " does not divide " + v1);
}
}
}
Existem 2 maneiras de corrigir o erro: tornar o método desejado estático ou criar uma instância da classe. Para escolher o método certo, pergunte-se se o método usa um campo ou outros métodos de classe. Se sim, então você deve criar uma instância da classe e chamar um método nela, caso contrário você deve tornar o método estático. Exemplo corrigido 1:
public class DivTest {
int modulus;
public DivTest(int m) {
modulus = m;
}
boolean divisible(int x) {
return (x % modulus == 0);
}
public static void main(String[] args) {
int v1 = 14;
int v2 = 9;
DivTest tester = new DivTest(v2);
if (tester.divisible(v1) {
System.out.println(v1 + " is a multiple of " + v2);
} else {
System.out.println(v2 + " does not divide " + v1);
}
}
}
Exemplo corrigido 2:
public class DivTest {
static boolean divisible(int x, int y) {
return (x % y == 0);
}
public static void main(String[] args) {
int v1 = 14;
int v2 = 9;
if (divisible(v1, v2)) {
System.out.println(v1 + " is a multiple of " + v2);
} else {
System.out.println(v2 + " does not divide " + v1);
}
}
}
10. Usando objetos da classe String como parâmetros de método.
Em Java, uma classejava.lang.String
armazena dados de string. No entanto, strings em Java
- têm permanência (ou seja, não podem ser alterados),
- são objetos.
public static void main(String args[]) {
String test1 = "Today is ";
appendTodaysDate(test1);
System.out.println(test1);
}
/* прим. редактора: закомментированный метод должен иметь модификатор
static (здесь автором допущена ошибка №9)
public void appendTodaysDate(String line) {
line = line + (new Date()).toString();
}
*/
public static void appendTodaysDate(String line) {
line = line + (new Date()).toString();
}
No exemplo acima, o aluno deseja alterar o valor de uma variável local test1
atribuindo um novo valor a um parâmetro line
em um método appendTodaysDate
. Naturalmente isso não funcionará. O significado line
mudará, mas o significado test1
permanecerá o mesmo. Este erro ocorre devido a um mal-entendido de que (1) objetos Java são sempre passados por referência e (2) strings em Java são imutáveis. Você precisa entender que objetos string nunca alteram seu valor e todas as operações em strings criam um novo objeto. Para corrigir o erro no exemplo acima, você precisa retornar uma string do método ou passar um objeto StringBuffer
como parâmetro para o método em vez de String
. Exemplo corrigido 1:
public static void main(String args[]) {
String test1 = "Today is ";
test1 = appendTodaysDate(test1);
System.out.println(test1);
}
public static String appendTodaysDate(String line) {
return (line + (new Date()).toString());
}
Exemplo corrigido 2:
public static void main(String args[]) {
StringBuffer test1 = new StringBuffer("Today is ");
appendTodaysDate(test1);
System.out.println(test1.toString());
}
public static void appendTodaysDate(StringBuffer line) {
line.append((new Date()).toString());
}
Aproximadamente. tradução |
11. Declarando um construtor como método
Os construtores de objetos em Java são semelhantes em aparência aos métodos regulares. As únicas diferenças são que o construtor não especifica o tipo de valor de retorno e o nome é igual ao nome da classe. Infelizmente, Java permite que o nome do método seja igual ao nome da classe. No exemplo abaixo, o aluno deseja inicializar um campo de classeVector list
ao criar a classe. Isso não acontecerá porque um método 'IntList'
não é um construtor. Exemplo errado.
public class IntList {
Vector list;
// Выглядит How конструктор, но на самом деле это метод
public void IntList() {
list = new Vector();
}
public append(int n) {
list.addElement(new Integer(n));
}
}
O código lançará uma exceção NullPointerException
na primeira vez que o campo for acessado list
. O erro é fácil de corrigir: você só precisa remover o valor de retorno do cabeçalho do método. Exemplo corrigido:
public class IntList {
Vector list;
// Это конструктор
public IntList() {
list = new Vector();
}
public append(int n) {
list.addElement(new Integer(n));
}
}
12. Esqueci de converter um objeto para o tipo necessário
Como todas as outras linguagens orientadas a objetos, em Java você pode referir-se a um objeto como sua superclasse. Isso é chamado'upcasting'
e é feito automaticamente em Java. Entretanto, se uma variável, campo de classe ou valor de retorno de método for declarado como uma superclasse, os campos e métodos da subclasse ficarão invisíveis. Referindo-se a uma superclasse como uma subclasse é chamada 'downcasting'
, você mesmo precisa registrá-la (ou seja, trazer o objeto para a subclasse desejada). Os alunos muitas vezes esquecem de criar subclasses de um objeto. Isso acontece com mais frequência ao usar matrizes de objetos e coleções de um pacote java.util
(ou seja, o Collection Framework ). O exemplo abaixo String
coloca um objeto em um array e depois o remove do array para compará-lo com outra string. O compilador detectará um erro e não compilará o código até que uma conversão de tipo seja especificada explicitamente. Exemplo errado.
Object arr[] = new Object[10];
arr[0] = "m";
arr[1] = new Character('m');
String arg = args[0];
if (arr[0].compareTo(arg) < 0) {
System.out.println(arg + " comes before " + arr[0]);
}
O significado da conversão de tipo é difícil para alguns. Os métodos dinâmicos costumam causar dificuldades. No exemplo acima, se o método tivesse sido usado equals
em vez de compareTo
, o compilador não teria gerado erro e o código teria funcionado corretamente, pois o método equals
da classe teria sido chamado String
. Você precisa entender que a vinculação dinâmica é diferente de downcasting
. Exemplo corrigido:
Object arr[] = new Object[10];
arr[0] = "m";
arr[1] = new Character('m');
String arg = args[0];
if ( ((String) arr[0]).compareTo(arg) < 0) {
System.out.println(arg + " comes before " + arr[0]);
}
13. Usando interfaces.
Para muitos estudantes, a diferença entre classes e interfaces não é totalmente clara. Portanto, alguns alunos tentam implementar interfaces comoObserver
ou Runnable
usando a palavra-chave extends em vez de implements . Para corrigir o erro, basta corrigir a palavra-chave para a correta. Exemplo errado:
public class SharkSim extends Runnable {
float length;
...
}
Exemplo corrigido:
public class SharkSim implements Runnable {
float length;
...
}
Erro relacionado: Ordem incorreta de blocos de extensão e implementação . De acordo com a especificação Java, as declarações de extensão de classe devem vir antes das declarações de implementação de interface. Além disso, para interfaces, a palavra-chave implements precisa ser escrita apenas uma vez; múltiplas interfaces são separadas por vírgulas. Mais alguns exemplos errôneos:
// Неправильный порядок
public class SharkSim implements Swimmer extends Animal {
float length;
...
}
// ключевое слово implements встречается несколько раз
public class DiverSim implements Swimmer implements Runnable {
int airLeft;
...
}
Exemplos corrigidos:
// Правильный порядок
public class SharkSim extends Animal implements Swimmer {
float length;
...
}
// Несколько интерфейсов разделяются запятыми
public class DiverSim implements Swimmer, Runnable {
int airLeft;
...
}
14. Esqueci de usar o valor de retorno de um método de superclasse
Java permite que você chame um método de superclasse semelhante de uma subclasse usando a palavra-chave palavra-chave. Às vezes, os alunos precisam chamar métodos de superclasse, mas muitas vezes esquecem de usar o valor de retorno. Isso acontece com frequência especialmente entre aqueles alunos que ainda não compreenderam os métodos e seus valores de retorno. No exemplo abaixo, um aluno deseja inserir o resultado de umtoString()
método de superclasse no resultado de um toString()
método de subclasse. No entanto, ele não usa o valor de retorno do método da superclasse. Exemplo errado:
public class GraphicalRectangle extends Rectangle {
Color fillColor;
boolean beveled;
...
public String toString() {
super();
return("color=" + fillColor + ", beveled=" + beveled);
}
}
Para corrigir o erro, geralmente é suficiente atribuir o valor de retorno a uma variável local e, em seguida, usar essa variável ao calcular o resultado do método da subclasse. Exemplo corrigido:
public class GraphicalRectangle extends Rectangle {
Color fillColor;
boolean beveled;
...
public String toString() {
String rectStr = super();
return(rectStr + " - " +
"color=" + fillColor + ", beveled=" + beveled);
}
}
15. Esqueci de adicionar componentes AWT
AWT usa um modelo de design de GUI simples: cada componente de interface deve primeiro ser criado usando seu próprio construtor e depois colocado na janela do aplicativo usando umadd()
método de componente pai. Assim, a interface no AWT recebe uma estrutura hierárquica. Os alunos às vezes esquecem essas duas etapas. Eles criam um componente mas esquecem de colocá-lo na janela de ampliação. Isso não causará erros na fase de compilação, o componente simplesmente não aparecerá na janela do aplicativo. Exemplo errado.
public class TestFrame extends Frame implements ActionListener {
public Button exit;
public TestFrame() {
super("Test Frame");
exit = new Button("Quit");
}
}
Para corrigir esse erro, basta adicionar os componentes aos seus pais. O exemplo abaixo mostra como fazer isso. Deve-se observar que muitas vezes um aluno que se esquece de adicionar um componente à janela de uma aplicação também se esquece de atribuir ouvintes de eventos a esse componente. Exemplo corrigido:
public class TestFrame extends Frame implements ActionListener {
public Button exit;
public TestFrame() {
super("Test Frame");
exit = new Button("Quit");
Panel controlPanel = new Panel();
controlPanel.add(exit);
add("Center", controlPanel);
exit.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}
17. Esqueci de iniciar a transmissão
Multithreading em Java é implementado usando ojava.lang.Thread
. O ciclo de vida de um thread consiste em 4 estágios: inicializado, iniciado, bloqueado e parado. O thread recém-criado está em um estado inicializado. Para colocá-lo em estado de execução, você precisa chamar seu arquivo start()
. Às vezes, os alunos criam tópicos, mas esquecem de iniciá-los. Geralmente o erro ocorre quando o aluno tem conhecimento insuficiente sobre programação paralela e multithreading. (tradução aproximada: não vejo a conexão) Para corrigir o erro, basta iniciar o thread. No exemplo abaixo, um aluno deseja criar a animação de uma imagem utilizando a interface Runnable
, mas esqueceu de iniciar o thread. Exemplo errado
public class AnimCanvas extends Canvas implements Runnable {
protected Thread myThread;
public AnimCanvas() {
myThread = new Thread(this);
}
// метод run() не будет вызван,
// потому что поток не запущен.
public void run() {
for(int n = 0; n < 10000; n++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
animateStep(n);
}
}
...
}
Exemplo corrigido:
public class AnimCanvas extends Canvas implements Runnable {
static final int LIMIT = 10000;
protected Thread myThread;
public AnimCanvas() {
myThread = new Thread(this);
myThread.start();
}
public void run() {
for(int n = 0; n < LIMIT; n++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
animateStep(n);
}
}
...
}
O ciclo de vida de um thread e o relacionamento entre threads e classes que implementam uma interface Runnable
é uma parte muito importante da programação Java e seria útil focar nisso.
18. Usando o método readLine() proibido da classe java.io.DataInputStream
readLine()
Na versão 1.0 do Java, você tinha que usar um método de classe para ler uma string de texto java.io.DataInputStream
. A versão 1.1 do Java adicionou um conjunto completo de classes de E/S para fornecer operações de E/S para texto: as classes Reader
e Writer
. Assim, a partir da versão 1.1 para ler uma linha de texto, deve-se utilizar o método readLine()
class java.io.BufferedReader
. Os alunos podem não estar cientes desta mudança, especialmente se foram ensinados a partir de livros mais antigos. (tradução aproximada: na verdade não é mais relevante. É improvável que alguém estude agora com livros de 10 anos). O método antigo readLine()
permanece no JDK, mas é declarado ilegal, o que muitas vezes confunde os alunos. O que você precisa entender é que usar um método readLine()
de classe java.io.DataInputStream
não é errado, apenas está desatualizado. Você deve usar a classe BufferedReader
. Exemplo errado:
public class LineReader {
private DataInputStream dis;
public LineReader(InputStream is) {
dis = new DataInputStream(is);
}
public String getLine() {
String ret = null;
try {
ret = dis.readLine(); // Неправильно! Запрещено.
} catch (IOException ie) { }
return ret;
}
}
Exemplo corrigido:
public class LineReader {
private BufferedReader br;
public LineReader(InputStream is) {
br = new BufferedReader(new InputStreamReader(is));
}
public String getLine() {
String ret = null;
try {
ret = br.readLine();
} catch (IOException ie) { }
return ret;
}
}
Existem outros métodos proibidos em versões posteriores à 1.0, mas este é o mais comum.
19. Usando double como float
Como a maioria das outras linguagens, Java suporta operações em números de ponto flutuante (números fracionários). Java possui 2 tipos primitivos para números de ponto flutuante:double
para números com precisão de 64 bits de acordo com o padrão IEEE e float
para números com precisão de 32 bits de acordo com o padrão IEEE. A dificuldade está em usar números decimais como 1,75, 12.9e17 ou -0,00003 - o compilador os atribui ao tipo double
. Java não executa conversões de tipo em operações onde pode ocorrer perda de precisão. Esta conversão de tipo deve ser feita pelo programador. Por exemplo, Java não permitirá que você atribua um valor de tipo a uma int
variável de tipo byte
sem uma conversão de tipo, conforme mostrado no exemplo abaixo.
byte byteValue1 = 17; /* неправильно! */
byte byteValue2 = (byte)19; /* правильно */
Como os números fracionários são representados por tipo double
e a atribuição double
a uma variável de tipo float
pode levar à perda de precisão, o compilador reclamará de qualquer tentativa de usar números fracionários como float
. Portanto, usar as tarefas abaixo impedirá a compilação da classe.
float realValue1 = -1.7; /* неправильно! */
float realValue2 = (float)(-1.9); /* правильно */
Essa atribuição funcionaria em C ou C++, mas em Java é muito mais rigorosa. Existem 3 maneiras de se livrar desse erro. Você pode usar type double
em vez de float
. Esta é a solução mais simples. Na verdade, não faz muito sentido usar aritmética de 32 bits em vez de 64 bits; a diferença de velocidade ainda é consumida pela JVM (além disso, nos processadores modernos, todos os números fracionários são convertidos para o formato de um processador de 80 bits cadastre-se antes de qualquer operação). A única vantagem de utilizá-los float
é que ocupam menos memória, o que é útil ao trabalhar com um grande número de variáveis fracionárias. Você pode usar um modificador de tipo de número para informar ao compilador como armazenar o número. Modificador para tipo float - 'f'
. Assim, o compilador atribuirá o tipo 1.75 a double
e 1.75f - float
. Por exemplo:
float realValue1 = 1.7; /* неправильно! */
float realValue2 = 1.9f; /* правильно */
Você pode usar conversão de tipo explícita. Esta é a maneira menos elegante, mas é útil ao converter uma variável de tipo double
em um tipo float
. Exemplo:
float realValue1 = 1.7f;
double realValue2 = 1.9;
realValue1 = (float)realValue2;
Você pode ler mais sobre números de ponto flutuante aqui e aqui.
- comentário do tradutor - |
GO TO FULL VERSION