JavaRush /Blogue Java /Random-PT /Erros de programadores Java iniciantes. Parte 2
articles
Nível 15

Erros de programadores Java iniciantes. Parte 2

Publicado no grupo Random-PT
Erros de programadores Java iniciantes. Parte 1

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ático main:
Erros de programadores Java iniciantes.  Parte 2 - 1
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 classe java.lang.Stringarmazena dados de string. No entanto, strings em Java
  1. têm permanência (ou seja, não podem ser alterados),
  2. são objetos.
Portanto, eles não podem ser tratados apenas como um buffer de caracteres; eles são objetos imutáveis. Às vezes, os alunos passam strings na expectativa equivocada de que o objeto string será passado como uma matriz de caracteres por referência (como em C ou C++). O compilador geralmente não considera isso um erro. Exemplo errado.
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 test1atribuindo um novo valor a um parâmetro lineem um método appendTodaysDate. Naturalmente isso não funcionará. O significado linemudará, mas o significado test1permanecerá 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 StringBuffercomo 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
Na verdade, não é tão fácil entender qual é o erro. Como os objetos são passados ​​por referência, significa lineque se referem ao mesmo lugar que test1. Isso significa que ao criar um novo line, criamos um novo.No test1exemplo errado, tudo parece que a transferência Stringé por valor e não por referência.

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 classe Vector listao 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 NullPointerExceptionna 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 Stringcoloca 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 equalsem vez de compareTo, o compilador não teria gerado erro e o código teria funcionado corretamente, pois o método equalsda 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 como Observerou Runnableusando 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 um toString()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 um add()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 o java.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 Readere 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.DataInputStreamnã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: doublepara números com precisão de 64 bits de acordo com o padrão IEEE e floatpara 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 intvariável de tipo bytesem 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 doublee a atribuição doublea uma variável de tipo floatpode 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 doubleem 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 doublee 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 doubleem 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 -
É isso.
No exemplo 10, realmente foi cometido o erro 9. Percebi imediatamente, mas esqueci de escrever uma nota. mas não o corrigiu para que não houvesse discrepâncias com a fonte original.

Autor: A.Grasoff™ Link para a fonte: Erros de programadores Java iniciantes
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION