JavaRush /Java Blog /Random-TL /Mga pagkakamali ng mga nagsisimulang java programmer. Bah...
articles
Antas

Mga pagkakamali ng mga nagsisimulang java programmer. Bahagi 2

Nai-publish sa grupo
Mga pagkakamali ng mga baguhan na java programmer. Bahagi 1

9. Pagtawag ng mga non-static na pamamaraan ng klase mula sa pangunahing() na pamamaraan

Ang entry point ng anumang Java program ay dapat na isang static na pamamaraan main:
Ошибки начинающих java-программистов. Часть 2 - 1
public static void main(String[] args) {
  ...
}
Dahil ang pamamaraang ito ay static, hindi ka makakatawag ng mga non-static na pamamaraan ng klase mula dito. Ang mga mag-aaral ay madalas na nakakalimutan tungkol dito at sinusubukang tumawag ng mga pamamaraan nang hindi gumagawa ng isang halimbawa ng klase. Ang pagkakamaling ito ay kadalasang ginagawa sa pinakasimula ng pagsasanay, kapag ang mga mag-aaral ay nagsusulat ng maliliit na programa. Maling halimbawa:
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);
        }
    }
}
Mayroong 2 paraan para ayusin ang error: gawing static ang gustong paraan o gumawa ng instance ng klase. Upang piliin ang tamang paraan, tanungin ang iyong sarili kung ang pamamaraan ay gumagamit ng isang field o iba pang mga pamamaraan ng klase. Kung oo, dapat kang lumikha ng isang halimbawa ng klase at tumawag ng isang pamamaraan dito, kung hindi, dapat mong gawing static ang pamamaraan. Nawastong halimbawa 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);
        }
    }
}
Nawastong halimbawa 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. Paggamit ng String class object bilang mga parameter ng pamamaraan.

Sa Java, ang isang klase java.lang.Stringay nag-iimbak ng data ng string. Gayunpaman, ang mga string sa Java
  1. may permanente (iyon ay, hindi sila mababago),
  2. ay mga bagay.
Samakatuwid, hindi sila maaaring ituring bilang isang buffer ng character lamang; sila ay mga hindi nababagong bagay. Minsan ang mga mag-aaral ay nagpapasa ng mga string sa maling pag-asa na ang string object ay ipapasa bilang isang character array sa pamamagitan ng reference (tulad ng sa C o C++). Karaniwang hindi ito itinuturing ng compiler na isang error. Maling halimbawa.
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();
}
Sa halimbawa sa itaas, gustong baguhin ng mag-aaral ang value ng isang lokal na variable test1sa pamamagitan ng pagtatalaga ng bagong value sa isang parameter linesa isang paraan appendTodaysDate. Natural na hindi ito gagana. Ang kahulugan lineay magbabago, ngunit ang kahulugan test1ay mananatiling pareho. Nangyayari ang error na ito dahil sa hindi pagkakaunawaan na ang (1) java object ay palaging ipinapasa sa pamamagitan ng reference at (2) mga string sa java ay hindi nababago. Kailangan mong maunawaan na ang mga bagay na string ay hindi kailanman nagbabago ng kanilang halaga, at ang lahat ng mga operasyon sa mga string ay lumikha ng isang bagong bagay. Upang ayusin ang error sa halimbawa sa itaas, kailangan mong ibalik ang isang string mula sa pamamaraan, o ipasa ang isang bagay StringBufferbilang parameter sa pamamaraan sa halip na String. Nawastong halimbawa 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());
}
Nawastong halimbawa 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());
}

tinatayang pagsasalin
Sa totoo lang, hindi ganoon kadaling maunawaan kung ano ang error. Dahil ang mga bagay ay ipinasa sa pamamagitan ng sanggunian, nangangahulugan ito linena tumutukoy ito sa parehong lugar bilang test1. Nangangahulugan ito na sa pamamagitan ng paggawa ng bago line, gumagawa kami ng bago test1. Sa maling halimbawa, ang lahat ay mukhang ang paglipat Stringay ayon sa halaga, at hindi sa pamamagitan ng sanggunian.

11. Pagdedeklara ng constructor bilang isang paraan

Конструкторы an objectов в Java внешне похожы на обычные методы. Единственные отличия - у конструктора не указывается тип возвращаемого значения и название совпадает с именем класса. К несчастью, Java допускает задание имени метода, совпадающего с названием класса. В примере ниже, студент хочет проинициализировать поле класса Vector list при создании класса. Этого не произойдет, так How метод 'IntList' - это не конструктор. Ошибочный пример.
public class IntList {
    Vector list;

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

    public append(int n) {
        list.addElement(new Integer(n));
    }
}
Код выдаст исключение NullPointerException при первом же ображении к полю list. Ошибку легко исправить: нужно просто убрать возвращаемое meaning из заголовка метода. Исправленный пример:
public class IntList {
    Vector list;

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

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

12. Забыл привести an object к нужному типу

Как и во всех других an objectно-ориентированных языках, в Java можно обращаться к an objectу How к его суперклассу. Это называется 'upcasting', он выполняется в Java автоматически. Однако, если переменная, поле класса or возвращаемое meaning метода объявлено How суперкласс, поля и методы подкласса будут невидимы. Обращение к суперклассу How к подклассу называется 'downcasting', его нужно прописывать самостоятельно (то есть привести an object к нужному подклассу). Студенты часто забывают о приведении оъекта к подклассу. Чаще всего это случается при использовании массивов an objectов Object и коллекций из пакета java.util (имеется ввиду Collection Framework). В примере ниже an object String заносится в массив, а затем извлекается из массива для сравнения с другой строкой. Компилятор обнаружит ошибку и не станет компorровать code, пока не будет явно указано приведение типов. Ошибочный пример.
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]);
}
Смысл приведения типов для некоторых оказывается затруднительным. Особенно часто затруднения вызывают динамические методы. В примере выше, если бы использовался метод equals instead of compareTo, компилятор бы не выдал ошибку, и code бы правильно отработал, так How вызвался бы метод equals именно класса String. Нужно понять, что динамическое связывание отличается от downcasting. Исправленный пример:
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. Использование интерфейсов.

Для многих студентов не совсем ясна разница между классами и интерфейсами. Поэтому, некоторые студенты пытаются реализовать интерфейсы, такие How Observer or Runnable, с помощью ключевого слова extends, instead of implements. Для исправления ошибки, нужно просто исправить ключевое слово на верное. Ошибочный пример:
public class SharkSim extends Runnable {
    float length;
    ...
}
Исправленный пример:
public class SharkSim implements Runnable {
    float length;
    ...
}
Связанная с этим ошибка: неправильный порядок блоков extends и implements. Согласно спецификации Java, объявление о расширении класса должно идти перед объявлениями о реализации интерфейсов. Также, для интерфейсов ключевое слово implements нужно писать всего 1 раз, несколько интерфейсов разделяются запятыми. Еще ряд ошибочных примеров:
// Неправильный порядок
public class SharkSim implements Swimmer extends Animal {
    float length;
    ...
}

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

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

14. Забыл использовать meaning, возвращаемое методом суперкласса

Java позволяет вызывать из подкласса аналогичный метод суперкласса с помощью ключевого слова keyword. Иногда студентам приходится вызывать методы суперкласса, но при этом часто они забывают использовать возвращаемое meaning. Особенно часто это случается у тех студентов, которые ещe не осмыслor методы и их возвращаемые значения. В примере ниже студент хочет вставить результат метода toString() суперкласса в результат метода toString() подкласса. При этом он не использует возвращаемое meaning метода суперкласса. Ошибочный пример:
public class GraphicalRectangle extends Rectangle {
      Color fillColor;
      boolean beveled;
      ...
      public String toString() {
          super();
          return("color=" + fillColor + ", beveled=" + beveled);
      }
}
Для исправления ошибки обычно достаточно присвоить возвращаемое meaning локальной переменной, и затем использовать эту переменную при вычислении результата метода подкласса. Исправленный пример:
public class GraphicalRectangle extends Rectangle {
      Color fillColor;
      boolean beveled;
      ...
      public String toString() {
          String rectStr = super();
          return(rectStr + " - " +
         "color=" + fillColor + ", beveled=" + beveled);
      }
}

15. Забыл добавить AWT компоненты

В AWT используется простая модель построения графического интерфейса: каждый компонент интерфейса должен быть сначала создан с помощью своего конструктора, а затем помещен в окно applications с помощью метода add() родительского компонента. Таким образом, интерфейс на AWT получает иерархическую структуру. Студенты иногда забывают об этих 2х шагах. Они создают компонент, но забывают разместить его в окне приожения. Это не вызовет ошибок на этапе компиляции, компонент просто не отобразится в окне applications. Ошибочный пример.
public class TestFrame extends Frame implements ActionListener {
    public Button exit;

    public TestFrame() {
        super("Test Frame");
        exit = new Button("Quit");
    }
}
Whatбы исправить эту ошибку, необходимо просто добавить компоненты к своим родителям. Пример ниже показывает, How это сделать. Необходимо заметить, что часто студент, забывший добавить компонент в окно applications, также забывает назначить слушателей событий для этого компонента. Исправленный пример:
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. Забыл запустить поток

Многопоточность в Java реализуется с помощью класса java.lang.Thread. Жизненный цикл потока состоит из 4х этапов: проинициализирован, запущен, заблокирован и остановлен. ТОлько что созданный поток находится в проинициализированном состоянии. Whatбы перевести его в запущенное состояние, необходимо вызвать его метод start(). Иногда студенты создают потоки, но забывают запустить их. Обычно ошибка возникает при недостаточных знаниях студента о параллельном программировании и многопоточности. (прим. перев.: не вижу связи) Whatбы исправить ошибку, необходимо просто запустить поток. В примере ниже, студент хочет создать анимацию картинки используя интерфейс Runnable, но он забыл запустить поток. Ошибочный пример
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);
                }
        }
        ...
}
Исправленный пример:
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);
                }
        }
        ...
}
Жизненный цикл потока и связь потоков и классов, реализующих интерфейс Runnable — это очень важная часть программирования на Java, и не будет лишним заострить свое внимание на этом.

18. Использование запрещенного метода readLine() класса java.io.DataInputStream

В Java версии 1.0 для считывания строки текста необходимо было использовать метод readLine() класса java.io.DataInputStream. В Java версии 1.1 был добавлен целый набор классов для ввода-вывода, обеспечивающий операции ввода-вывода для текста: классы Reader и Writer. Таким образом с версии 1.1 для чтения строки текста надо использовать метод readLine() класса java.io.BufferedReader. Студенты могут не знать об этом изменении, особенно если они обучались по старым книгам. (прим. перев. вообще-то уже не актуально. вряд ли кто-то станет сейчас учиться по книгам 10-летней давности). Старый метод readLine() оставлен в JDK, но объявлен How запрещенный, что часто смущает студентов. Необходимо понять, что использование метода readLine() класса java.io.DataInputStream не является неправильным, оно просто устарело. Необходимо использовать класс BufferedReader. Ошибочный пример:
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;
    }
}
Исправленный пример:
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;
    }
}
Есть и другие запрещенные методы в versionх, более поздних чем 1.0, но этот встречается чаще всего.

19. Использование типа double How float

Как и в большинстве других языков, в Java поддерживаются операции над числами с плавающей точкой (дробными числами). В Java есть 2 типа-примитива для чисел с плавающей точкой: double для чисел с 64-bit точностью по стандарту IEEE, и float, для чисел с 32-bit точностью по стандарту IEEE. Трудность заключается в использовании десятичных чисел, таких How 1.75, 12.9e17 or -0.00003 — компилятор присваивает им тип double. Java не производит приведение типов в операциях, в которых может произойти потеря точности. Такое приведение типов должен осуществлять программист. Например, Java не позволит присвоить meaning типа int переменной типа byte без приведения типов, How показано в примере ниже.
byte byteValue1 = 17; /* неправильно! */
byte byteValue2 = (byte)19; /* правильно */
Так How дробные числа представлены типом double, и присваивание double переменной типа float может привести к потере точности, компилятор пожалуется на любую попытку использовать дробные числа How float. Так что использование присваиваний, приведенных ниже, не даст классу откомпorроваться.
float realValue1 = -1.7;          /* неправильно! */
float realValue2 = (float)(-1.9); /* правильно */
Это присваивание сработало бы в C or C++, для Java все гораздо строже. Есть 3 способа избавиться от этой ошибки. Можно использовать тип double instead of типа float. Это наиболее простое решение. На самом деле нет особого смысла использовать 32-битную арифметику instead of 64-bit, разницу в скорости все равно скушает JVM (к тому же в современных процессорах все дробные числа приводятся к формату 80-битного регистра процессора перед любой операцией). Единственный плюс использования float — это то, что они занимают меньше памяти, что бывает полезно при работе с большим числом дробных переменых. Можно использовать модификатор для обозначения типа числа, чтобы сообщить компилятору How хранить число. Модификатор для типа float - 'f'. Таким образом, компилятор присвоит числу 1.75 тип double, а 1.75f - float. Например:
float realValue1 = 1.7;    /* неправильно! */
float realValue2 = 1.9f;   /* правильно */
Можно использовать явное приведение типов. Это наименее элегантный способ, но он полезен при конвертации переменной типа double в тип float. Пример:
float realValue1 = 1.7f;
double realValue2 = 1.9;
realValue1 = (float)realValue2;
Подробнее о числах с плавающей точкой можно почитать здесь и здесь.

-- комментарий переводчика --
Все.
В примере 10 на самом деле допущена ошибка 9. я ее сразу заметил, но забыл написать примечание. а исправлять не стал чтобы не было расхождений с первоисточником.

Author: А.Грасоff™ Ссылка на первоисточник: Ошибки начинающих java-программистов
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION