JavaRush /Java-Blog /Random-DE /Fehler von Anfänger-Java-Programmierern. Teil 2
articles
Level 15

Fehler von Anfänger-Java-Programmierern. Teil 2

Veröffentlicht in der Gruppe Random-DE
Fehler von Anfänger-Java-Programmierern. Teil 1

9. Aufruf nicht statischer Klassenmethoden über die Methode main()

Der Einstiegspunkt jedes Java-Programms sollte eine statische Methode sein main:
Fehler von Anfänger-Java-Programmierern.  Teil 2 - 1
public static void main(String[] args) {
  ...
}
Da diese Methode statisch ist, können Sie von ihr aus keine nicht statischen Klassenmethoden aufrufen. Studenten vergessen dies oft und versuchen, Methoden aufzurufen, ohne eine Instanz der Klasse zu erstellen. Dieser Fehler wird normalerweise gleich zu Beginn der Ausbildung gemacht, wenn die Schüler kleine Programme schreiben. Falsches Beispiel:
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);
        }
    }
}
Es gibt zwei Möglichkeiten, den Fehler zu beheben: Machen Sie die gewünschte Methode statisch oder erstellen Sie eine Instanz der Klasse. Um die richtige Methode auszuwählen, fragen Sie sich, ob die Methode ein Feld oder andere Klassenmethoden verwendet. Wenn ja, sollten Sie eine Instanz der Klasse erstellen und eine Methode dafür aufrufen, andernfalls sollten Sie die Methode statisch machen. Korrigiertes Beispiel 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);
        }
    }
}
Korrigiertes Beispiel 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. Verwendung von String-Klassenobjekten als Methodenparameter.

In Java java.lang.Stringspeichert eine Klasse Zeichenfolgendaten. Allerdings Strings in Java
  1. haben Beständigkeit (das heißt, sie können nicht verändert werden),
  2. sind Objekte.
Daher können sie nicht nur als Zeichenpuffer behandelt werden; es handelt sich um unveränderliche Objekte. Manchmal übergeben Schüler Zeichenfolgen in der irrigen Erwartung, dass das Zeichenfolgenobjekt per Referenz als Zeichenarray übergeben wird (wie in C oder C++). Der Compiler betrachtet dies normalerweise nicht als Fehler. Falsches Beispiel.
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();
}
Im obigen Beispiel möchte der Student den Wert einer lokalen Variablen ändern, indem er einem Parameter in einer Methode test1einen neuen Wert zuweist . Das wird natürlich nicht funktionieren. Die Bedeutung wird sich ändern, aber die Bedeutung bleibt dieselbe. Dieser Fehler tritt aufgrund eines Missverständnisses auf, dass (1) Java-Objekte immer als Referenz übergeben werden und (2) Zeichenfolgen in Java unveränderlich sind. Sie müssen verstehen, dass String-Objekte ihren Wert nie ändern und alle Operationen an Strings ein neues Objekt erstellen. Um den Fehler im obigen Beispiel zu beheben, müssen Sie entweder eine Zeichenfolge von der Methode zurückgeben oder anstelle von ein Objekt als Parameter an die Methode übergeben . Korrigiertes Beispiel 1:lineappendTodaysDatelinetest1StringBufferString
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());
}
Korrigiertes Beispiel 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. Übersetzung
Eigentlich ist es nicht so einfach, den Fehler zu verstehen. Da Objekte als Referenz übergeben werden, bedeutet dies, linedass sie auf denselben Ort verweisen wie test1. Das heißt, indem wir etwas Neues erstellen line, erstellen wir ein Neues test1. Im falschen Beispiel sieht alles so aus, als ob die Übertragung Stringnach Wert und nicht nach Referenz erfolgt.

11. Einen Konstruktor als Methode deklarieren

Objektkonstruktoren in Java ähneln im Aussehen regulären Methoden. Die einzigen Unterschiede bestehen darin, dass der Konstruktor den Typ des Rückgabewerts nicht angibt und der Name mit dem Klassennamen übereinstimmt. Leider erlaubt Java, dass der Methodenname mit dem Klassennamen übereinstimmt. Im folgenden Beispiel möchte der Schüler beim Erstellen der Klasse ein Klassenfeld initialisieren Vector list. Dies wird nicht passieren, da eine Methode 'IntList'kein Konstruktor ist. Falsches Beispiel.
public class IntList {
    Vector list;

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

    public append(int n) {
        list.addElement(new Integer(n));
    }
}
Der Code löst NullPointerExceptionbeim ersten Zugriff auf das Feld eine Ausnahme aus list. Der Fehler lässt sich leicht beheben: Sie müssen lediglich den Rückgabewert aus dem Methodenheader entfernen. Korrigiertes Beispiel:
public class IntList {
    Vector list;

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

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

12. Ich habe vergessen, ein Objekt in den erforderlichen Typ umzuwandeln

Wie in allen anderen objektorientierten Sprachen können Sie in Java ein Objekt als seine Oberklasse bezeichnen. Dies nennt man 'upcasting', es erfolgt automatisch in Java. Wenn jedoch eine Variable, ein Klassenfeld oder ein Methodenrückgabewert als Oberklasse deklariert wird, sind die Felder und Methoden der Unterklasse unsichtbar. Wenn Sie eine Superklasse als Unterklasse bezeichnen 'downcasting', müssen Sie sie selbst registrieren (d. h. das Objekt in die gewünschte Unterklasse bringen). Schüler vergessen oft, ein Objekt in Unterklassen zu unterteilen. Dies geschieht am häufigsten, wenn Arrays von Objekten und Sammlungen aus einem Paket java.util(d. h. dem Collection Framework ) verwendet werden. Das folgende Beispiel Stringfügt ein Objekt in ein Array ein und entfernt es dann aus dem Array, um es mit einer anderen Zeichenfolge zu vergleichen. Der Compiler erkennt einen Fehler und kompiliert den Code erst, wenn explizit eine Typumwandlung angegeben wird. Falsches Beispiel.
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]);
}
Für einige ist die Bedeutung des Typcastings schwierig. Besonders dynamische Methoden bereiten häufig Schwierigkeiten. Wenn im obigen Beispiel die Methode equalsanstelle von verwendet worden wäre compareTo, hätte der Compiler keinen Fehler ausgegeben und der Code hätte korrekt funktioniert, da die Methode equalsder Klasse aufgerufen worden wäre String. Sie müssen verstehen, dass sich dynamische Verknüpfungen von unterscheiden downcasting. Korrigiertes Beispiel:
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. Verwendung von Schnittstellen.

Für viele Studierende ist der Unterschied zwischen Klassen und Schnittstellen nicht ganz klar. Daher versuchen einige Schüler, Schnittstellen wie Observeroder Runnablemithilfe des Schlüsselworts „extens“ anstelle von „implements“ zu implementieren . Um den Fehler zu beheben, müssen Sie lediglich das Schlüsselwort durch das richtige ersetzen. Falsches Beispiel:
public class SharkSim extends Runnable {
    float length;
    ...
}
Korrigiertes Beispiel:
public class SharkSim implements Runnable {
    float length;
    ...
}
Zugehöriger Fehler: Falsche Reihenfolge der Extends- und Implements- Blöcke . Gemäß der Java-Spezifikation müssen Klassenerweiterungsdeklarationen vor Schnittstellenimplementierungsdeklarationen stehen. Außerdem muss das Schlüsselwort „implements“ für Schnittstellen nur einmal geschrieben werden; mehrere Schnittstellen werden durch Kommas getrennt. Einige weitere fehlerhafte Beispiele:
// Неправильный порядок
public class SharkSim implements Swimmer extends Animal {
    float length;
    ...
}

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

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

14. Ich habe vergessen, den Rückgabewert einer Superklassenmethode zu verwenden

Mit Java können Sie mithilfe des Schlüsselworts keyword eine ähnliche Superklassenmethode aus einer Unterklasse aufrufen. Manchmal müssen Schüler Superklassenmethoden aufrufen, vergessen aber oft, den Rückgabewert zu verwenden. Dies geschieht besonders häufig bei Studierenden, die Methoden und deren Rückgabewerte noch nicht verstanden haben. Im folgenden Beispiel möchte ein Schüler das Ergebnis einer toString()Oberklassenmethode in das Ergebnis einer toString()Unterklassenmethode einfügen. Der Rückgabewert der Superklassenmethode wird jedoch nicht verwendet. Falsches Beispiel:
public class GraphicalRectangle extends Rectangle {
      Color fillColor;
      boolean beveled;
      ...
      public String toString() {
          super();
          return("color=" + fillColor + ", beveled=" + beveled);
      }
}
Um den Fehler zu beheben, reicht es normalerweise aus, den Rückgabewert einer lokalen Variablen zuzuweisen und diese Variable dann bei der Berechnung des Ergebnisses der Unterklassenmethode zu verwenden. Korrigiertes Beispiel:
public class GraphicalRectangle extends Rectangle {
      Color fillColor;
      boolean beveled;
      ...
      public String toString() {
          String rectStr = super();
          return(rectStr + " - " +
         "color=" + fillColor + ", beveled=" + beveled);
      }
}

15. Ich habe vergessen, AWT-Komponenten hinzuzufügen

AWT verwendet ein einfaches GUI-Designmodell: Jede Schnittstellenkomponente muss zunächst mit einem eigenen Konstruktor erstellt und dann mithilfe einer add()übergeordneten Komponentenmethode im Anwendungsfenster platziert werden. Dadurch erhält die Schnittstelle auf AWT eine hierarchische Struktur. Manchmal vergessen Schüler diese beiden Schritte. Sie erstellen eine Komponente, vergessen aber, sie im Vergrößerungsfenster zu platzieren. Dies führt nicht zu Fehlern in der Kompilierungsphase; die Komponente wird einfach nicht im Anwendungsfenster angezeigt. Falsches Beispiel.
public class TestFrame extends Frame implements ActionListener {
    public Button exit;

    public TestFrame() {
        super("Test Frame");
        exit = new Button("Quit");
    }
}
Um diesen Fehler zu beheben, müssen Sie lediglich die Komponenten zu ihren übergeordneten Komponenten hinzufügen. Das folgende Beispiel zeigt, wie das geht. Es ist zu beachten, dass ein Student, der vergisst, eine Komponente zu einem Anwendungsfenster hinzuzufügen, oft auch vergisst, dieser Komponente Ereignis-Listener zuzuweisen. Korrigiertes Beispiel:
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. Ich habe vergessen, den Stream zu starten

Multithreading in Java wird mithilfe von . implementiert java.lang.Thread. Der Lebenszyklus eines Threads besteht aus 4 Phasen: initialisiert, gestartet, blockiert und gestoppt. Der neu erstellte Thread befindet sich in einem initialisierten Zustand. Um es in einen laufenden Zustand zu versetzen, müssen Sie es aufrufen start(). Manchmal erstellen Schüler Threads, vergessen aber, sie zu starten. Normalerweise tritt der Fehler auf, wenn der Schüler nicht über ausreichende Kenntnisse in paralleler Programmierung und Multithreading verfügt. (ca. übersetzt: Ich sehe den Zusammenhang nicht) Um den Fehler zu beheben, reicht es aus, den Thread zu starten. Im folgenden Beispiel möchte ein Schüler mithilfe der Benutzeroberfläche eine Animation eines Bildes erstellen Runnable, hat jedoch vergessen, den Thread zu starten. Falsches Beispiel
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);
                }
        }
        ...
}
Korrigiertes Beispiel:
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);
                }
        }
        ...
}
Der Lebenszyklus eines Threads und die Beziehung zwischen Threads und Klassen, die eine Schnittstelle implementieren, Runnableist ein sehr wichtiger Teil der Java-Programmierung, und es wäre sinnvoll, sich darauf zu konzentrieren.

18. Verwendung der verbotenen readLine() -Methode der Klasse java.io.DataInputStream

readLine()In Java Version 1.0 mussten Sie eine Klassenmethode verwenden , um eine Textzeichenfolge zu lesen java.io.DataInputStream. Java Version 1.1 hat eine ganze Reihe von I/O-Klassen hinzugefügt, um I/O-Operationen für Text bereitzustellen: die Readerund Writer. Daher müssen Sie ab Version 1.1 zum Lesen einer Textzeile die readLine()Klassenmethode verwenden java.io.BufferedReader. Die Schüler sind sich dieser Änderung möglicherweise nicht bewusst, insbesondere wenn sie anhand älterer Bücher unterrichtet wurden. (ungefähre Übersetzung: Eigentlich nicht mehr relevant. Es ist unwahrscheinlich, dass jetzt jemand aus Büchern lernt, die 10 Jahre alt sind.) Die alte Methode readLine()verbleibt im JDK, wird aber für illegal erklärt, was Studenten oft verwirrt. Sie müssen verstehen, dass die Verwendung einer readLine()Klassenmethode java.io.DataInputStreamnicht falsch, sondern lediglich veraltet ist. Sie müssen die Klasse verwenden BufferedReader. Falsches Beispiel:
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;
    }
}
Korrigiertes Beispiel:
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;
    }
}
In Versionen nach 1.0 gibt es noch andere verbotene Methoden, diese ist jedoch die häufigste.

19. Double als Float verwenden

Wie die meisten anderen Sprachen unterstützt Java Operationen mit Gleitkommazahlen (Bruchzahlen). Java verfügt über zwei primitive Typen für Gleitkommazahlen: doubleIEEE 64-Bit-Präzision und floatIEEE 32-Bit-Präzision. Die Schwierigkeit besteht darin, dass Dezimalzahlen wie 1,75, 12,9e17 oder -0,00003 vom Compiler dem Typ zugewiesen werden double. Java führt keine Typumwandlungen in Vorgängen durch, bei denen es zu Präzisionsverlusten kommen kann. Diese Typumwandlung muss vom Programmierer durchgeführt werden. Beispielsweise lässt Java es nicht zu, einer Typvariablen intohne byteTypumwandlung einen Typwert zuzuweisen, wie im folgenden Beispiel gezeigt.
byte byteValue1 = 17; /* неправильно! */
byte byteValue2 = (byte)19; /* правильно */
Da Bruchzahlen durch den Typ dargestellt werden doubleund die Zuweisung doublean eine Variable vom Typ floatzu einem Genauigkeitsverlust führen kann, beschwert sich der Compiler über jeden Versuch, Bruchzahlen als zu verwenden float. Wenn Sie also die folgenden Aufgaben verwenden, kann die Klasse nicht kompiliert werden.
float realValue1 = -1.7;          /* неправильно! */
float realValue2 = (float)(-1.9); /* правильно */
Diese Zuweisung würde in C oder C++ funktionieren, in Java ist sie jedoch viel strenger. Es gibt drei Möglichkeiten, diesen Fehler zu beheben. Sie können type doubleanstelle von verwenden float. Dies ist die einfachste Lösung. Tatsächlich macht es wenig Sinn, 32-Bit-Arithmetik anstelle von 64-Bit zu verwenden; der Geschwindigkeitsunterschied wird immer noch von der JVM aufgefressen (außerdem werden in modernen Prozessoren alle Bruchzahlen in das Format eines 80-Bit-Prozessors umgewandelt). vor jeder Operation registrieren). Der einzige Vorteil ihrer Verwendung floatbesteht darin, dass sie weniger Speicher beanspruchen, was bei der Arbeit mit einer großen Anzahl gebrochener Variablen nützlich ist. Sie können einen Zahlentypmodifikator verwenden, um dem Compiler mitzuteilen, wie die Zahl gespeichert werden soll. Modifikator für Typ float - 'f'. Daher weist der Compiler , doubleund den Typ 1.75 zu 1.75f - float. Zum Beispiel:
float realValue1 = 1.7;    /* неправильно! */
float realValue2 = 1.9f;   /* правильно */
Sie können eine explizite Typumwandlung verwenden. Dies ist die am wenigsten elegante Methode, aber sie ist nützlich, wenn Sie eine Typvariable doublein einen Typ konvertieren float. Beispiel:
float realValue1 = 1.7f;
double realValue2 = 1.9;
realValue1 = (float)realValue2;
Mehr über Gleitkommazahlen können Sie hier und hier lesen.

-- Kommentar des Übersetzers --
Das ist es.
Im Beispiel 10 wurde tatsächlich Fehler 9 gemacht. Ich habe es sofort bemerkt, aber vergessen, eine Notiz zu schreiben. Es wurde jedoch nicht korrigiert, so dass es keine Unstimmigkeiten mit der Originalquelle geben würde.

Autor: A.Grasoff™ Link zur Quelle: Fehler von Java-Programmieranfängern
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION