JavaRush /Blog Java /Random-ES /Errores de los programadores principiantes de Java. Parte...
articles
Nivel 15

Errores de los programadores principiantes de Java. Parte 2

Publicado en el grupo Random-ES
Errores de los programadores principiantes de Java. Parte 1

9. Llamar a métodos de clase no estáticos desde el método main()

El punto de entrada de cualquier programa Java debería ser un método estático main:
Errores de los programadores principiantes de Java.  Parte 2 - 1

public static void main(String[] args) {
  ...
}
Dado que este método es estático, no puede llamar a métodos de clase no estáticos desde él. Los estudiantes a menudo se olvidan de esto e intentan llamar métodos sin crear una instancia de la clase. Este error suele cometerse al comienzo de la formación, cuando los estudiantes escriben programas pequeños. Ejemplo equivocado:

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);
        }
    }
}
Hay 2 formas de corregir el error: hacer estático el método deseado o crear una instancia de la clase. Para elegir el método correcto, pregúntese si el método utiliza un campo u otros métodos de clase. En caso afirmativo, entonces debe crear una instancia de la clase y llamar a un método en ella; de lo contrario, debe hacer que el método sea estático. Ejemplo corregido 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);
        }
    }
}
Ejemplo corregido 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. Usar objetos de clase String como parámetros de método.

En Java, una clase java.lang.Stringalmacena datos de cadena. Sin embargo, las cadenas en Java
  1. tener permanencia (es decir, no se pueden cambiar),
  2. son objetos.
Por lo tanto, no pueden tratarse simplemente como un búfer de caracteres; son objetos inmutables. A veces, los estudiantes pasan cadenas con la expectativa errónea de que el objeto de cadena se pasará como una matriz de caracteres por referencia (como en C o C++). El compilador normalmente no considera que esto sea un error. Ejemplo equivocado.

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();
}
En el ejemplo anterior, el estudiante quiere cambiar el valor de una variable local test1asignando un nuevo valor a un parámetro lineen un método appendTodaysDate. Naturalmente esto no funcionará. El significado linecambiará, pero test1seguirá siendo el mismo. Este error se produce debido a un malentendido de que (1) los objetos java siempre se pasan por referencia y (2) las cadenas en java son inmutables. Debe comprender que los objetos de cadena nunca cambian su valor y que todas las operaciones en cadenas crean un nuevo objeto. Para corregir el error en el ejemplo anterior, debe devolver una cadena del método o pasar un objeto StringBuffercomo parámetro al método en lugar de String. Ejemplo corregido 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());
}
Ejemplo corregido 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());
}

aprox. traducción
En realidad, no es tan fácil entender cuál es el error. Dado que los objetos se pasan por referencia, significa lineque se refieren al mismo lugar que test1. Esto significa que al crear uno nuevo line, creamos uno nuevo test1... En el ejemplo incorrecto, todo parece como si la transferencia Stringfuera por valor y no por referencia.

11. Declarar un constructor como método

Los constructores de objetos en Java son similares en apariencia a los métodos normales. Las únicas diferencias son que el constructor no especifica el tipo de valor de retorno y el nombre es el mismo que el nombre de la clase. Desafortunadamente, Java permite que el nombre del método sea el mismo que el nombre de la clase. En el siguiente ejemplo, el estudiante desea inicializar un campo de clase Vector listal crear la clase. Esto no sucederá porque un método 'IntList'no es un constructor. Ejemplo equivocado.

public class IntList {
    Vector list;

    // Выглядит Cómo конструктор, но на самом деле это метод
    public void IntList() {
        list = new Vector();
    }

    public append(int n) {
        list.addElement(new Integer(n));
    }
}
El código generará una excepción NullPointerExceptionla primera vez que se acceda al campo list. El error es fácil de solucionar: sólo necesita eliminar el valor de retorno del encabezado del método. Ejemplo corregido:

public class IntList {
    Vector list;

    // Это конструктор
    public IntList() {
        list = new Vector();
    }

    public append(int n) {
        list.addElement(new Integer(n));
    }
}

12. Olvidé convertir un objeto al tipo requerido

Como todos los demás lenguajes orientados a objetos, en Java puedes referirte a un objeto como su superclase. Esto se llama 'upcasting', se hace automáticamente en Java. Sin embargo, si una variable, campo de clase o valor de retorno de un método se declara como superclase, los campos y métodos de la subclase serán invisibles. Hacer referencia a una superclase como subclase se llama 'downcasting', debe registrarla usted mismo (es decir, llevar el objeto a la subclase deseada). Los estudiantes a menudo se olvidan de subclasificar un objeto. Esto sucede con mayor frecuencia cuando se utilizan matrices de objetos y colecciones de un paquete java.util(es decir, Collection Framework ). El siguiente ejemplo Stringcoloca un objeto en una matriz y luego lo elimina de la matriz para compararlo con otra cadena. El compilador detectará un error y no compilará el código hasta que se especifique explícitamente una conversión de tipo. Ejemplo equivocado.

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]);
}
El significado de conversión de tipos es difícil para algunos. Los métodos dinámicos suelen causar dificultades. En el ejemplo anterior, si se hubiera utilizado el método equalsen lugar de compareTo, el compilador no habría arrojado un error y el código habría funcionado correctamente, ya que se habría llamado al método equalsde la clase String. Debe comprender que los enlaces dinámicos son diferentes a los downcasting. Ejemplo corregido:

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. Uso de interfaces.

Para muchos estudiantes, la diferencia entre clases e interfaces no está del todo clara. Por lo tanto, algunos estudiantes intentan implementar interfaces como Observero Runnableusando la palabra clave extends en lugar de implements . Para corregir el error, sólo necesita corregir la palabra clave por la correcta. Ejemplo equivocado:

public class SharkSim extends Runnable {
    float length;
    ...
}
Ejemplo corregido:

public class SharkSim implements Runnable {
    float length;
    ...
}
Error relacionado: Orden incorrecto de bloques de extensión e implementación . Según la especificación de Java, las declaraciones de extensión de clase deben ir antes que las declaraciones de implementación de interfaz. Además, para las interfaces, la palabra clave implements debe escribirse solo una vez; varias interfaces están separadas por comas. Algunos ejemplos más erróneos:

// Неправильный порядок
public class SharkSim implements Swimmer extends Animal {
    float length;
    ...
}

// ключевое слово implements встречается несколько раз
public class DiverSim implements Swimmer implements Runnable {
    int airLeft;
    ...
}
Ejemplos corregidos:

// Правильный порядок
public class SharkSim extends Animal implements Swimmer {
    float length;
    ...
}

// Несколько интерфейсов разделяются запятыми
public class DiverSim implements Swimmer, Runnable {
    int airLeft;
    ...
}

14. Olvidé usar el valor de retorno de un método de superclase.

Java le permite llamar a un método de superclase similar desde una subclase usando la palabra clave palabra clave. A veces los estudiantes tienen que llamar a métodos de superclase, pero a menudo se olvidan de utilizar el valor de retorno. Esto sucede especialmente entre aquellos estudiantes que aún no han comprendido los métodos y sus valores de retorno. En el siguiente ejemplo, un estudiante quiere insertar el resultado de un toString()método de superclase en el resultado de un toString()método de subclase. Sin embargo, no utiliza el valor de retorno del método de superclase. Ejemplo equivocado:

public class GraphicalRectangle extends Rectangle {
      Color fillColor;
      boolean beveled;
      ...
      public String toString() {
          super();
          return("color=" + fillColor + ", beveled=" + beveled);
      }
}
Para corregir el error, normalmente es suficiente asignar el valor de retorno a una variable local y luego usar esa variable al calcular el resultado del método de la subclase. Ejemplo corregido:

public class GraphicalRectangle extends Rectangle {
      Color fillColor;
      boolean beveled;
      ...
      public String toString() {
          String rectStr = super();
          return(rectStr + " - " +
         "color=" + fillColor + ", beveled=" + beveled);
      }
}

15. Olvidé agregar componentes AWT

AWT utiliza un modelo de diseño de GUI simple: cada componente de la interfaz debe crearse primero usando su propio constructor y luego colocarse en la ventana de la aplicación usando un add()método de componente principal. Por tanto, la interfaz en AWT recibe una estructura jerárquica. Los estudiantes a veces olvidan estos 2 pasos. Crean un componente pero se olvidan de colocarlo en la ventana de ampliación. Esto no causará errores en la etapa de compilación; el componente simplemente no aparecerá en la ventana de la aplicación. Ejemplo equivocado.

public class TestFrame extends Frame implements ActionListener {
    public Button exit;

    public TestFrame() {
        super("Test Frame");
        exit = new Button("Quit");
    }
}
Para corregir este error, simplemente necesita agregar los componentes a sus padres. El siguiente ejemplo muestra cómo hacer esto. Cabe señalar que, a menudo, un estudiante que olvida agregar un componente a la ventana de una aplicación también olvida asignar detectores de eventos a ese componente. Ejemplo corregido:

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. Olvidé iniciar la transmisión.

El multiproceso en Java se implementa utilizando java.lang.Thread. El ciclo de vida de un hilo consta de 4 etapas: inicializado, iniciado, bloqueado y detenido. El hilo recién creado está en un estado inicializado. Para ponerlo en estado de ejecución, debe llamarlo start(). A veces los estudiantes crean hilos pero se olvidan de iniciarlos. Generalmente el error ocurre cuando el estudiante no tiene conocimientos suficientes sobre programación paralela y multihilo. (aprox. traducción: no veo la conexión) Para corregir el error, solo necesitas iniciar el hilo. En el siguiente ejemplo, un estudiante quiere crear una animación de una imagen usando la interfaz Runnable, pero olvidó iniciar el hilo. Ejemplo equivocado

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);
                }       
        }
        ...
}
Ejemplo corregido:

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);
                }
        }
        ...
}
El ciclo de vida de un hilo y la relación entre hilos y clases que implementan una interfaz Runnablees una parte muy importante de la programación Java y sería útil centrarse en esto.

18. Uso del método prohibido readLine() de la clase java.io.DataInputStream

readLine()En la versión 1.0 de Java, había que utilizar un método de clase para leer una cadena de texto java.io.DataInputStream. La versión 1.1 de Java agregó un conjunto completo de clases de E/S para proporcionar operaciones de E/S para texto: Readery Writer. Así, a partir de la versión 1.1 para leer una línea de texto, debes utilizar el método readLine()de clase java.io.BufferedReader. Es posible que los estudiantes no se den cuenta de este cambio, especialmente si se les enseñó con libros más antiguos. (Traducción aproximada: en realidad ya no es relevante. Es poco probable que alguien estudie ahora libros que tenían 10 años). El antiguo método readLine()permanece en el JDK, pero se declara ilegal, lo que a menudo confunde a los estudiantes. Lo que debes entender es que usar un método readLine()de clase java.io.DataInputStreamno está mal, simplemente está desactualizado. Debes usar la clase BufferedReader. Ejemplo equivocado:

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;
    }
}
Ejemplo corregido:

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;
    }
}
Existen otros métodos prohibidos en versiones posteriores a la 1.0, pero este es el más común.

19. Usando doble como flotador

Como la mayoría de los otros lenguajes, Java admite operaciones con números de punto flotante (números fraccionarios). Java tiene dos tipos primitivos para números de punto flotante: doubleprecisión IEEE de 64 bits y floatprecisión IEEE de 32 bits. La dificultad es que cuando se utilizan números decimales como 1,75, 12,9e17 o -0,00003, el compilador los asigna al tipo double. Java no realiza conversiones de tipos en operaciones en las que puede producirse una pérdida de precisión. Este tipo de conversión debe ser realizado por el programador. Por ejemplo, Java no le permitirá asignar un valor de tipo a una intvariable de tipo bytesin una conversión de tipo, como se muestra en el siguiente ejemplo.

byte byteValue1 = 17; /* неправильно! */
byte byteValue2 = (byte)19; /* правильно */
Dado que los números fraccionarios se representan mediante tipo doubley la asignación doublea una variable de tipo floatpuede provocar una pérdida de precisión, el compilador se quejará de cualquier intento de utilizar números fraccionarios como float. Por lo tanto, utilizar las tareas a continuación evitará que la clase se compile.

float realValue1 = -1.7;          /* неправильно! */
float realValue2 = (float)(-1.9); /* правильно */
Esta asignación funcionaría en C o C++, pero en Java es mucho más estricta. Hay 3 formas de deshacerse de este error. Puedes usar tipo doubleen lugar de float. Ésta es la solución más sencilla. De hecho, no tiene mucho sentido utilizar aritmética de 32 bits en lugar de 64 bits; la diferencia de velocidad todavía la consume la JVM (además, en los procesadores modernos todos los números fraccionarios se convierten al formato de un procesador de 80 bits registrarse antes de cualquier operación). La única ventaja de usarlos floates que ocupan menos memoria, lo que resulta útil cuando se trabaja con una gran cantidad de variables fraccionarias. Puede utilizar un modificador de tipo de número para indicarle al compilador cómo almacenar el número. Modificador para tipo float - 'f'. Por lo tanto, el compilador asignará el tipo 1.75 a doubley 1.75f - float. Por ejemplo:

float realValue1 = 1.7;    /* неправильно! */
float realValue2 = 1.9f;   /* правильно */
Puede utilizar la conversión de tipos explícita. Esta es la forma menos elegante, pero es útil al convertir una variable de tipo doubleen un tipo float. Ejemplo:

float realValue1 = 1.7f; 
double realValue2 = 1.9;
realValue1 = (float)realValue2;
Puede leer más sobre los números de punto flotante aquí y aquí.

-- comentario del traductor --
Eso es todo.
En el ejemplo 10, en realidad se cometió el error 9. Lo noté de inmediato, pero olvidé escribir una nota. pero no lo corrigió para que no hubiera discrepancias con la fuente original.

Autor: A.Grasoff™ Enlace a la fuente: Errores de los programadores principiantes de Java
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION