JavaRush /Java Blog /Random-IT /Errori dei programmatori Java principianti. Parte 2
articles
Livello 15

Errori dei programmatori Java principianti. Parte 2

Pubblicato nel gruppo Random-IT
Errori dei programmatori Java principianti. Parte 1

9. Chiamare metodi di classe non statici dal metodo main()

Il punto di ingresso di qualsiasi programma Java dovrebbe essere un metodo statico main:
Errori dei programmatori Java principianti.  Parte 2 - 1
public static void main(String[] args) {
  ...
}
Poiché questo metodo è statico, non è possibile richiamare metodi di classe non statici da esso. Gli studenti spesso se ne dimenticano e provano a chiamare metodi senza creare un'istanza della classe. Questo errore viene solitamente commesso all'inizio della formazione, quando gli studenti scrivono piccoli programmi. Esempio sbagliato:
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);
        }
    }
}
Esistono 2 modi per correggere l'errore: rendere statico il metodo desiderato o creare un'istanza della classe. Per scegliere il metodo giusto, chiediti se il metodo utilizza un campo o altri metodi di classe. Se sì, dovresti creare un'istanza della classe e chiamare un metodo su di essa, altrimenti dovresti rendere il metodo statico. Esempio corretto 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);
        }
    }
}
Esempio corretto 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. Utilizzo degli oggetti della classe String come parametri del metodo.

In Java, una classe java.lang.Stringmemorizza dati di tipo stringa. Tuttavia, le stringhe in Java
  1. hanno permanenza (cioè non possono essere modificati),
  2. sono oggetti.
Pertanto, non possono essere trattati semplicemente come un buffer di caratteri; sono oggetti immutabili. A volte gli studenti passano le stringhe nell'errata aspettativa che l'oggetto stringa venga passato come un array di caratteri per riferimento (come in C o C++). Il compilatore di solito non lo considera un errore. Esempio sbagliato.
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();
}
Nell'esempio sopra, lo studente desidera modificare il valore di una variabile locale test1assegnando un nuovo valore a un parametro linein un metodo appendTodaysDate. Naturalmente questo non funzionerà. Il significato linecambierà, ma il significato test1rimarrà lo stesso. Questo errore si verifica a causa di un malinteso secondo cui (1) gli oggetti Java vengono sempre passati per riferimento e (2) le stringhe in Java sono immutabili. Devi capire che gli oggetti stringa non cambiano mai il loro valore e tutte le operazioni sulle stringhe creano un nuovo oggetto. Per correggere l'errore nell'esempio precedente, è necessario restituire una stringa dal metodo o passare un oggetto StringBuffercome parametro al metodo anziché String. Esempio corretto 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());
}
Esempio corretto 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());
}

ca. traduzione
In realtà non è così semplice capire quale sia l’errore. Poiché gli oggetti vengono passati per riferimento, significa lineche si riferiscono allo stesso posto di test1. Ciò significa che creandone uno nuovo line, ne creiamo uno nuovo test1. Nell'esempio sbagliato, tutto sembra come se il trasferimento Stringavvenisse per valore e non per riferimento.

11. Dichiarare un costruttore come metodo

I costruttori di oggetti in Java hanno un aspetto simile ai metodi normali. Le uniche differenze sono che il costruttore non specifica il tipo di valore restituito e il nome è lo stesso del nome della classe. Sfortunatamente, Java consente che il nome del metodo sia uguale al nome della classe. Nell'esempio seguente, lo studente desidera inizializzare un campo della classe Vector listdurante la creazione della classe. Ciò non accadrà perché un metodo 'IntList'non è un costruttore. Esempio sbagliato.
public class IntList {
    Vector list;

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

    public append(int n) {
        list.addElement(new Integer(n));
    }
}
Il codice genererà un'eccezione NullPointerExceptionla prima volta che si accede al campo list. L'errore è facile da correggere: devi solo rimuovere il valore restituito dall'intestazione del metodo. Esempio corretto:
public class IntList {
    Vector list;

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

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

12. Ho dimenticato di eseguire il cast di un oggetto nel tipo richiesto

Come tutti gli altri linguaggi orientati agli oggetti, in Java puoi fare riferimento a un oggetto come alla sua superclasse. Questo si chiama 'upcasting', viene eseguito automaticamente in Java. Tuttavia, se una variabile, un campo di classe o un valore restituito da un metodo viene dichiarato come superclasse, i campi e i metodi della sottoclasse saranno invisibili. Facendo riferimento a una superclasse come viene chiamata una sottoclasse 'downcasting', è necessario registrarla tu stesso (ovvero portare l'oggetto nella sottoclasse desiderata). Gli studenti spesso dimenticano di creare sottoclassi per un oggetto. Ciò accade molto spesso quando si utilizzano array di oggetti e raccolte da un pacchetto java.util(ovvero il Collection Framework ). L'esempio seguente Stringinserisce un oggetto in un array e quindi lo rimuove dall'array per confrontarlo con un'altra stringa. Il compilatore rileverà un errore e non compilerà il codice finché non verrà specificato esplicitamente un cast di tipo. Esempio sbagliato.
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]);
}
Il significato del type casting è difficile per alcuni. I metodi dinamici soprattutto spesso causano difficoltà. Nell'esempio sopra, se il metodo fosse stato utilizzato equalsal posto di compareTo, il compilatore non avrebbe generato errori e il codice avrebbe funzionato correttamente, poiché il metodo equalsdella classe si sarebbe chiamato String. È necessario comprendere che il collegamento dinamico è diverso da downcasting. Esempio corretto:
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. Utilizzo delle interfacce.

Per molti studenti la differenza tra classi e interfacce non è del tutto chiara. Pertanto, alcuni studenti provano a implementare interfacce come Observero Runnableutilizzando la parola chiave extends invece di implements . Per correggere l'errore, devi solo correggere la parola chiave con quella corretta. Esempio sbagliato:
public class SharkSim extends Runnable {
    float length;
    ...
}
Esempio corretto:
public class SharkSim implements Runnable {
    float length;
    ...
}
Errore correlato: ordine errato dei blocchi extends e implements . Secondo le specifiche Java, le dichiarazioni di estensione della classe devono precedere le dichiarazioni di implementazione dell'interfaccia. Inoltre, per le interfacce, la parola chiave implements deve essere scritta una sola volta; più interfacce sono separate da virgole. Alcuni esempi più errati:
// Неправильный порядок
public class SharkSim implements Swimmer extends Animal {
    float length;
    ...
}

// ключевое слово implements встречается несколько раз
public class DiverSim implements Swimmer implements Runnable {
    int airLeft;
    ...
}
Esempi corretti:
// Правильный порядок
public class SharkSim extends Animal implements Swimmer {
    float length;
    ...
}

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

14. Ho dimenticato di utilizzare il valore restituito da un metodo della superclasse

Java ti consente di chiamare un metodo di superclasse simile da una sottoclasse utilizzando la parola chiave parola chiave. A volte gli studenti devono chiamare i metodi della superclasse, ma spesso dimenticano di utilizzare il valore restituito. Ciò accade soprattutto spesso tra quegli studenti che non hanno ancora compreso i metodi e i loro valori di ritorno. Nell'esempio seguente, uno studente desidera inserire il risultato di un toString()metodo della superclasse nel risultato di un toString()metodo della sottoclasse. Tuttavia, non utilizza il valore restituito dal metodo della superclasse. Esempio sbagliato:
public class GraphicalRectangle extends Rectangle {
      Color fillColor;
      boolean beveled;
      ...
      public String toString() {
          super();
          return("color=" + fillColor + ", beveled=" + beveled);
      }
}
Per correggere l'errore, di solito è sufficiente assegnare il valore restituito a una variabile locale, e quindi utilizzare quella variabile quando si calcola il risultato del metodo della sottoclasse. Esempio corretto:
public class GraphicalRectangle extends Rectangle {
      Color fillColor;
      boolean beveled;
      ...
      public String toString() {
          String rectStr = super();
          return(rectStr + " - " +
         "color=" + fillColor + ", beveled=" + beveled);
      }
}

15. Ho dimenticato di aggiungere i componenti AWT

AWT utilizza un semplice modello di progettazione GUI: ogni componente dell'interfaccia deve prima essere creato utilizzando il proprio costruttore e quindi inserito nella finestra dell'applicazione utilizzando un add()metodo del componente principale. Pertanto, l'interfaccia su AWT riceve una struttura gerarchica. Gli studenti a volte dimenticano questi 2 passaggi. Creano un componente ma dimenticano di posizionarlo nella finestra di ingrandimento. Ciò non causerà errori in fase di compilazione; il componente semplicemente non apparirà nella finestra dell'applicazione. Esempio sbagliato.
public class TestFrame extends Frame implements ActionListener {
    public Button exit;

    public TestFrame() {
        super("Test Frame");
        exit = new Button("Quit");
    }
}
Per correggere questo errore, devi semplicemente aggiungere i componenti ai loro genitori. L'esempio seguente mostra come eseguire questa operazione. Va notato che spesso uno studente che dimentica di aggiungere un componente alla finestra dell'applicazione dimentica anche di assegnare gli ascoltatori di eventi a quel componente. Esempio corretto:
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. Ho dimenticato di avviare lo streaming

Il multithreading in Java viene implementato utilizzando java.lang.Thread. Il ciclo di vita di un thread è composto da 4 fasi: inizializzato, avviato, bloccato e interrotto. Il thread appena creato è in uno stato inizializzato. Per metterlo in uno stato di esecuzione, è necessario chiamarlo start(). A volte gli studenti creano discussioni ma dimenticano di avviarle. Di solito l'errore si verifica quando lo studente non ha una conoscenza sufficiente della programmazione parallela e del multithreading. (trad. ca.: non vedo il collegamento) Per correggere l'errore è sufficiente avviare il thread. Nell'esempio seguente, uno studente desidera creare un'animazione di un'immagine utilizzando l'interfaccia Runnable, ma ha dimenticato di avviare la discussione. Esempio sbagliato
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);
                }
        }
        ...
}
Esempio corretto:
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);
                }
        }
        ...
}
Il ciclo di vita di un thread e la relazione tra thread e classi che implementano un'interfaccia Runnableè una parte molto importante della programmazione Java, e su questo sarebbe utile soffermarsi.

18. Utilizzo del metodo proibito readLine() della classe java.io.DataInputStream

readLine()Nella versione Java 1.0, dovevi utilizzare un metodo di classe per leggere una stringa di testo java.io.DataInputStream. Java versione 1.1 ha aggiunto un intero set di classi I/O per fornire operazioni di I/O per il testo: Readere Writer. Pertanto, dalla versione 1.1 per leggere una riga di testo è necessario utilizzare il metodo readLine()class java.io.BufferedReader. Gli studenti potrebbero non essere consapevoli di questo cambiamento, soprattutto se hanno imparato da libri più vecchi. (circa. Traduzione: in realtà non più rilevante. È improbabile che qualcuno ora studi su libri vecchi di 10 anni). Il vecchio metodo readLine()rimane nel JDK, ma è dichiarato illegale, il che spesso confonde gli studenti. Quello che devi capire è che usare un metodo readLine()di classe java.io.DataInputStreamnon è sbagliato, è semplicemente obsoleto. È necessario utilizzare la classe BufferedReader. Esempio sbagliato:
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;
    }
}
Esempio corretto:
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;
    }
}
Esistono altri metodi vietati nelle versioni successive alla 1.0, ma questo è il più comune.

19. Usare double come float

Come la maggior parte degli altri linguaggi, Java supporta operazioni su numeri in virgola mobile (numeri frazionari). Java ha due tipi primitivi per i numeri a virgola mobile: doubleprecisione IEEE a 64 bit e floatprecisione IEEE a 32 bit. La difficoltà è quando si utilizzano numeri decimali come 1.75, 12.9e17 o -0.00003: il compilatore li assegna al tipo double. Java non esegue cast di tipo nelle operazioni in cui potrebbe verificarsi una perdita di precisione. Questo tipo di casting deve essere fatto dal programmatore. Ad esempio, Java non consentirà di assegnare un valore di tipo a una intvariabile di tipo bytesenza un cast di tipo, come mostrato nell'esempio seguente.
byte byteValue1 = 17; /* неправильно! */
byte byteValue2 = (byte)19; /* правильно */
Poiché i numeri frazionari sono rappresentati da tipo doublee l'assegnazione doublea una variabile di tipo floatpuò portare a una perdita di precisione, il compilatore si lamenterà di qualsiasi tentativo di utilizzare numeri frazionari come float. Pertanto, l'utilizzo dei compiti seguenti impedirà la compilazione della classe.
float realValue1 = -1.7;          /* неправильно! */
float realValue2 = (float)(-1.9); /* правильно */
Questo compito funzionerebbe in C o C++, ma in Java è molto più rigoroso. Esistono 3 modi per eliminare questo errore. Puoi usare type doubleinvece di float. Questa è la soluzione più semplice. In effetti, non ha molto senso usare l'aritmetica a 32 bit invece che a 64 bit; la differenza di velocità è ancora assorbita dalla JVM (inoltre, nei processori moderni tutti i numeri frazionari vengono convertiti nel formato di un processore a 80 bit registrarsi prima di ogni operazione). L'unico vantaggio di utilizzarli floatè che occupano meno memoria, il che è utile quando si lavora con un gran numero di variabili frazionarie. È possibile utilizzare un modificatore del tipo numerico per indicare al compilatore come memorizzare il numero. Modificatore per tipo float - 'f'. Pertanto, il compilatore assegnerà il tipo 1.75 a double, e 1.75f - float. Per esempio:
float realValue1 = 1.7;    /* неправильно! */
float realValue2 = 1.9f;   /* правильно */
È possibile utilizzare il casting di tipo esplicito. Questo è il modo meno elegante, ma è utile quando si converte una variabile di tipo doublein un tipo float. Esempio:
float realValue1 = 1.7f;
double realValue2 = 1.9;
realValue1 = (float)realValue2;
Puoi leggere ulteriori informazioni sui numeri in virgola mobile qui e qui.

-- commento del traduttore --
Questo è tutto.
Nell'esempio 10 in realtà è stato commesso l'errore 9. Me ne sono accorto subito, ma mi sono dimenticato di scrivere una nota. ma non lo corresse in modo che non ci fossero discrepanze con la fonte originale.

Autore: A.Grasoff™ Link alla fonte: Errori dei programmatori Java principianti
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION