JavaRush /Java Blog /Random-TK /Başlangyç java programmistleriniň ýalňyşlyklary. 2-nji bö...
articles
Dereje

Başlangyç java programmistleriniň ýalňyşlyklary. 2-nji bölüm

Toparda çap edildi
Başlangyç java programmistleriniň ýalňyşlyklary. 1-nji bölüm

9. Esasy () usuldan statik däl synp usullaryny çagyrmak

Islendik Java programmasynyň giriş nokady statiki usul bolmaly main:
Ошибки начинающих java-программистов. Часть 2 - 1
public static void main(String[] args) {
  ...
}
Bu usul statik bolansoň, ondan statik däl synp usullaryny çagyryp bilmersiňiz. Okuwçylar köplenç muny ýatdan çykarýarlar we synpyň mysalyny döretmezden usullary çagyrmaga synanyşýarlar. Bu ýalňyşlyk, adatça, okuwçylar kiçi programmalar ýazanda, okuwyň başynda goýberilýär. Nädogry mysal:
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);
        }
    }
}
Erroralňyşlygy düzetmegiň 2 usuly bar: islenýän usuly statik etmek ýa-da synpyň mysalyny döretmek. Dogry usuly saýlamak üçin usulyň meýdan ýa-da beýleki synp usullaryny ulanýandygyny özüňize soraň. Hawa bolsa, synpyň mysalyny döretmeli we oňa bir usul çagyrmaly, ýogsam usuly statik etmeli. Dogry mysal 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);
        }
    }
}
Dogry mysal 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. String synp obýektlerini usul parametrleri hökmünde ulanmak.

Java-da bir synp java.lang.Stringsimli maglumatlary saklaýar. Şeýle-de bolsa, Java-da setirler
  1. hemişelikligi bar (ýagny üýtgedip bolmaz),
  2. obýektlerdir.
Şonuň üçin olara diňe bir nyşan buferi hökmünde seredip bolmaz, üýtgewsiz zatlar. Käwagt talyplar setir obýektiniň simwol massiwine (C ýa-da C ++ bolşy ýaly) geçer diýen ýalňyş umyt bilen setirleri geçýärler. Düzüji, adatça, bu ýalňyşlyk hasaplamaýar. Nädogry mysal.
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();
}
Aboveokardaky mysalda, okuwçy bir usulda test1parametrlere täze baha bellemek bilen ýerli üýtgeýjiniň bahasyny üýtgetmek isleýär . Elbetde, bu netije bermez. Manysy üýtgär, ýöne manysy öňküligine galar. Bu ýalňyşlyk (1) java obýektleriniň elmydama salgylanma arkaly geçýändigi we (2) java setirleriniň üýtgemezligi sebäpli ýüze çykýar. Setli obýektleriň hiç haçan bahasyny üýtgetmejekdigine we setirlerdäki ähli amallaryň täze bir obýekt döredýändigine düşünmeli. Aboveokardaky mysaldaky ýalňyşlygy düzetmek üçin ýa-da usuldan bir setir yzyna gaýtarmaly, ýa-da ýerine ýerine parametr hökmünde bir obýekt geçirmeli . Dogry mysal 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());
}
Dogry mysal 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());
}

takmynan terjime
Aslynda, ýalňyşlygyň nämedigine düşünmek beýle aňsat däl. Obýektler salgylanma arkaly geçýändigi sebäpli, lineşol bir ýeri aňladýar test1. Diýmek, täzesini döretmek bilen linetäzesini döredýäris test1. Nädogry mysalda, hemme zat Stringsalgylanma däl-de, gymmaty boýunça bolup görünýär.

11. Usuly hökmünde konstruktory yglan etmek

Конструкторы 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-программистов
Teswirler
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION