JavaRush /Java блогы /Random-KK /Жаңадан бастаған java бағдарламашыларының қателері. 2-бөл...
articles
Деңгей

Жаңадан бастаған java бағдарламашыларының қателері. 2-бөлім

Топта жарияланған
Жаңадан бастаған java бағдарламашыларының қателері. 1 бөлім

9. Негізгі() әдісінен статикалық емес класс әдістерін шақыру

Кез келген Java бағдарламасының кіру нүктесі статикалық әдіс болуы керек main:
Жаңадан бастаған java бағдарламашыларының қателері.  2 - 1 бөлім
public static void main(String[] args) {
  ...
}
Бұл әдіс статикалық болғандықтан, одан статикалық емес класс әдістерін шақыра алмайсыз. Оқушылар бұл туралы жиі ұмытып, сынып данасын жасамай әдістерді шақыруға тырысады. Бұл қате әдетте оқытудың ең басында, студенттер шағын бағдарламаларды жазғанда жіберіледі. Қате мысал:
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);
        }
    }
}
Қатені түзетудің 2 жолы бар: қажетті әдісті статикалық ету немесе сынып данасын жасау. Дұрыс әдісті таңдау үшін, әдіс өрісті немесе басқа сынып әдістерін пайдаланады ма деп өзіңізден сұраңыз. Егер иә болса, онда сыныптың данасын жасап, оған әдісті шақыру керек, әйтпесе әдісті статикалық ету керек. Түзетілген мысал 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);
        }
    }
}
Түзетілген мысал 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 класының нысандарын әдіс параметрлері ретінде пайдалану.

Java тілінде сынып java.lang.Stringжол деректерін сақтайды. Дегенмен, Java тіліндегі жолдар
  1. тұрақтылығы бар (яғни оларды өзгерту мүмкін емес),
  2. an objectілер болып табылады.
Сондықтан оларды тек таңба буфері ретінде қарастыруға болмайды; олар өзгермейтін нысандар. Кейде студенттер жол нысаны сілтеме бойынша таңбалар массиві ретінде (C немесе C++ тіліндегі сияқты) жіберіледі деп қате күтумен жолдарды өткізеді. Компилятор әдетте бұл қате деп есептемейді. Дұрыс емес мысал.
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();
}
Жоғарыдағы мысалда студент әдістегі test1параметрге жаңа мән тағайындау арқылы жергілікті айнымалының мәнін өзгерткісі келеді . Әрине, бұл жұмыс істемейді. Мағынасы өзгереді, бірақ мағынасы сол қалпында қалады. Бұл қате (1) java нысандары әрқашан сілтеме арқылы берілетінін және (2) java жолының өзгермейтінін түсінбеу салдарынан орын алады. Жол нысандары ешқашан өз мәнін өзгертпейтінін және жолдардағы барлық операциялар жаңа нысанды жасайтынын түсінуіңіз керек. Жоғарыдағы мысалдағы қатені түзету үшін әдістен жолды қайтару керек немесе нысанды параметр ретінде әдіске орнына беру керек . Түзетілген мысал 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());
}
Түзетілген мысал 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());
}

шамамен. аударма
Шындығында, қатенің не екенін түсіну оңай емес. Нысандар сілтеме арқылы берілетіндіктен, бұл lineоның орнына сілтеме жасайтынын білдіреді test1. Бұл жаңасын жасау арқылы lineбіз жаңасын жасайтынымызды білдіреді.Қате test1мысалда барлығы тасымалдау Stringсілтеме бойынша емес, мән бойынша болып көрінеді.

11. Конструкторды әдіс ретінде жариялау

Java тіліндегі an object конструкторлары сыртқы түрі бойынша кәдімгі әдістерге ұқсас. Жалғыз айырмашылық - конструктор қайтарылатын мәннің түрін көрсетпейді және атау сынып атауымен бірдей. Өкінішке орай, Java әдіс атауының сынып атауымен бірдей болуына мүмкіндік береді. Төмендегі мысалда студент Vector listсыныпты жасау кезінде сынып өрісін инициализациялағысы келеді. Бұл болмайды, себебі әдіс 'IntList'конструктор емес. Дұрыс емес мысал.
public class IntList {
    Vector list;

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

    public append(int n) {
        list.addElement(new Integer(n));
    }
}
NullPointerExceptionӨріске бірінші рет кірген кезде code ерекше жағдайды шығарады list. Қатені түзету оңай: әдіс тақырыбынан қайтару мәнін жою қажет. Түзетілген мысал:
public class IntList {
    Vector list;

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

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

12. Объектіні қажетті түрге шығаруды ұмытып қалдыңыз

Барлық басқа an objectіге бағытталған тілдер сияқты, Java тілінде an objectіні оның суперклассы ретінде атауға болады. 'upcasting'Бұл Java тілінде автоматты түрде орындалады деп аталады . Дегенмен, айнымалы, сынып өрісі немесе әдіс қайтару мәні суперкласс ретінде жарияланған болса, ішкі сыныптың өрістері мен әдістері көрінбейтін болады. Ішкі сынып ретінде суперклассқа сілтеме жасау 'downcasting', оны өзіңіз тіркеуіңіз керек (яғни an objectіні қажетті қосалқы сыныпқа әкеліңіз). Студенттер an objectіні ішкі классификациялауды жиі ұмытады. Бұл көбінесе Нысандардың массивтерін және бумадағы жинақтарды пайдаланған кезде болады ( Жинау шеңберінjava.util білдіреді ). Төмендегі мысал нысанды массивке қояды, содан кейін оны басқа жолмен салыстыру үшін массивтен жояды. Компилятор қатені анықтайды және трансляция түрі нақты көрсетілгенше codeты құрастырмайды. Дұрыс емес мысал.String
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Жоғарыда келтірілген мысалда, егер әдіс орнына қолданылса compareTo, компилятор қате жібермес еді және code дұрыс жұмыс істейтін еді, өйткені 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. Интерфейстерді пайдалану.

Көптеген студенттер үшін сыныптар мен интерфейстер арасындағы айырмашылық толығымен анық емес. Сондықтан, кейбір студенттер интерфейстерді жүзеге асыруға тырысады, мысалы , Observerextensions кілт сөзінRunnable қолданады . Қатені түзету үшін кілт сөзді дұрыс сөзге түзету керек. Қате мысал:
public class SharkSim extends Runnable {
    float length;
    ...
}
Түзетілген мысал:
public class SharkSim implements Runnable {
    float length;
    ...
}
Қатысты қате: кеңейту және іске асыру блоктарының қате реті . Java спецификациясына сәйкес сынып кеңейтімі туралы мәлімдемелер интерфейсті іске асыру мәлімдемелерінен бұрын келуі керек. Сондай-ақ, интерфейстер үшін implements кілт сөзі тек бір рет жазылуы керек; бірнеше интерфейстер үтірмен бөлінген. Тағы бірнеше қате мысалдар:
// Неправильный порядок
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. Суперкласс әдісінің қайтару мәнін пайдалануды ұмытып қалдыңыз

Java кілт сөзді пайдаланып ішкі сыныптан ұқсас суперкласс әдісін шақыруға мүмкіндік береді. Кейде студенттер суперкласс әдістерін шақыруы керек, бірақ көбінесе қайтарылатын мәнді пайдалануды ұмытып кетеді. Бұл әсіресе әдістер мен олардың қайтару мәндерін әлі түсінбеген студенттер арасында жиі кездеседі. Төмендегі мысалда оқушы toString()суперкласс әдісінің нәтижесін ішкі сынып әдісінің нәтижесіне кірістіргісі келеді toString(). Дегенмен, ол суперкласс әдісінің қайтару мәнін пайдаланбайды. Қате мысал:
public class GraphicalRectangle extends Rectangle {
      Color fillColor;
      boolean beveled;
      ...
      public String toString() {
          super();
          return("color=" + fillColor + ", beveled=" + beveled);
      }
}
Қатені түзету үшін әдетте жергілікті айнымалыға қайтару мәнін тағайындау жеткілікті, содан кейін ішкі сынып әдісінің нәтижесін есептеу кезінде сол айнымалы мәнді пайдаланыңыз. Түзетілген мысал:
public class GraphicalRectangle extends Rectangle {
      Color fillColor;
      boolean beveled;
      ...
      public String toString() {
          String rectStr = super();
          return(rectStr + " - " +
         "color=" + fillColor + ", beveled=" + beveled);
      }
}

15. AWT құрамдастарын қосуды ұмытып қалдыңыз

add()AWT қарапайым GUI дизайн үлгісін пайдаланады: әрбір интерфейс құрамдас бөлігі алдымен өзінің конструкторы арқылы жасалуы керек, содан кейін негізгі компонент әдісін пайдаланып қолданба терезесіне орналастырылуы керек . Осылайша, AWT интерфейсі иерархиялық құрылымды алады. Оқушылар кейде осы 2 қадамды ұмытып кетеді. Олар құрамдас жасайды, бірақ оны үлкейту терезесіне қоюды ұмытады. Бұл компиляция сатысында қателерді тудырмайды, компонент қолданба терезесінде көрінбейді. Дұрыс емес мысал.
public class TestFrame extends Frame implements ActionListener {
    public Button exit;

    public TestFrame() {
        super("Test Frame");
        exit = new Button("Quit");
    }
}
Бұл қатені түзету үшін құрамдастарды олардың ата-аналарына қосу керек. Төмендегі мысал мұны қалай жасау керектігін көрсетеді. Көбінесе қолданба терезесіне компонент қосуды ұмытатын студент сол компонентке оқиға тыңдаушыларын тағайындауды ұмытып кететінін ескеру қажет. Түзетілген мысал:
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 кезеңнен тұрады: инициализацияланған, іске қосылған, блокталған және тоқтатылған. Жаңадан жасалған ағын инициализацияланған күйде. Оны іске қосу күйіне қою үшін оны шақыру керек start(). Кейде студенттер ағындарды жасайды, бірақ оларды бастауды ұмытып кетеді. Әдетте қате студенттің параллельді бағдарламалау және көп ағынды туралы білімдері жеткіліксіз болған кезде пайда болады. (шамамен аударма: қосылымды көрмеймін) Қатені түзету үшін ағынды бастау керек. Төмендегі мысалда студент интерфейсті пайдаланып суреттің анимациясын жасағысы келеді 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);
                }
        }
        ...
}
Жіптің өмірлік циклі және интерфейсті жүзеге асыратын ағындар мен сыныптар арасындағы байланыс RunnableJava бағдарламалауының өте маңызды бөлігі болып табылады және осыған назар аудару пайдалы болар еді.

18. java.io.DataInputStream сыныбының тыйым салынған readLine() әдісін пайдалану

Java 1.0 нұсқасында мәтін жолын оқу үшін readLine()сынып әдісін пайдалану керек болды java.io.DataInputStream. Java 1.1 нұсқасы мәтінге енгізу/шығару операцияларын қамтамасыз ету үшін енгізу/шығару сыныптарының тұтас жинағын қосты: Readerжәне Writer. Осылайша, 1.1 нұсқасынан бастап мәтін жолын оқу үшін readLine()сынып әдісін пайдалану керек java.io.BufferedReader. Студенттер бұл өзгерісті білмеуі мүмкін, әсіресе олар ескі кітаптардан оқытылған болса. (шамамен. Аударма: іс жүзінде енді өзекті емес. Енді ешкімнің 10 жастағы кітаптардан оқитыны екіталай). Ескі әдіс readLine()JDK-де қалады, бірақ заңсыз деп жарияланды, бұл студенттерді жиі шатастырады. Түсінуіңіз керек, 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;
    }
}
1.0 нұсқасынан кейінгі нұсқаларда басқа тыйым салынған әдістер бар, бірақ бұл ең кең таралған.

19. Қосарлауды қалқыма ретінде пайдалану

Көптеген басқа тілдер сияқты, Java өзгермелі нүкте сандарымен (бөлшек сандар) операцияларды қолдайды. Java тілінде өзгермелі нүктелі сандар үшін екі қарабайыр түрі бар: doubleIEEE 64-биттік дәлдік және floatIEEE 32-биттік дәлдік. 1.75, 12.9e17 немесе -0.00003 сияқты ондық сандарды пайдалану қиындығы - компилятор оларды типке тағайындайды double. Java дәлдік жоғалуы мүмкін операцияларда типті трансляцияларды орындамайды. Бұл түрдегі кастингті бағдарламашы жасау керек. Мысалы, Java төмендегі мысалда көрсетілгендей түрді трансляциясыз түрдегі intайнымалыға түр мәнін тағайындауға рұқсат бермейді .byte
byte byteValue1 = 17; /* неправильно! */
byte byteValue2 = (byte)19; /* правильно */
Бөлшек сандар түрі арқылы ұсынылатындықтан doubleжәне doubleтүрдегі айнымалыға тағайындау floatдәлдікті жоғалтуға әкелуі мүмкін болғандықтан, компилятор бөлшек сандарды ретінде пайдалану әрекетіне шағымданады float. Сондықтан төмендегі тапсырмаларды пайдалану сыныпты құрастыруға жол бермейді.
float realValue1 = -1.7;          /* неправильно! */
float realValue2 = (float)(-1.9); /* правильно */
Бұл тапсырма C немесе C++ тілінде жұмыс істейді, бірақ Java тілінде бұл әлдеқайда қатаң. Бұл қателіктен құтылудың 3 жолы бар. doubleорнына type пайдалануға болады float. Бұл ең қарапайым шешім. Шындығында, 64-биттік емес, 32-биттік арифметиканы пайдаланудың мәні жоқ; жылдамдықтағы айырмашылық әлі де JVM-мен жейді (бұдан басқа, қазіргі заманғы процессорларда барлық бөлшек сандар 80-биттік процессор форматына түрлендіріледі. кез келген операция алдында тіркелу). Оларды пайдаланудың бірден-бір артықшылығы float- олар аз жадты алады, бұл бөлшек айнымалылардың үлкен санымен жұмыс істегенде пайдалы. Компиляторға нөмірді қалай сақтау керектігін айту үшін сан түрінің модификаторын пайдалануға болады. Түрге арналған модификатор float - 'f'. doubleОсылайша, компилятор 1.75 түрін , және деп тағайындайды 1.75f - float. Мысалы:
float realValue1 = 1.7;    /* неправильно! */
float realValue2 = 1.9f;   /* правильно */
Сіз анық түрдегі трансляцияны пайдалана аласыз. Бұл ең аз талғампаз әдіс, бірақ ол түр айнымалысын doubletype түріне түрлендіру кезінде пайдалы float. Мысалы:
float realValue1 = 1.7f;
double realValue2 = 1.9;
realValue1 = (float)realValue2;
Жылжымалы нүкте сандары туралы толығырақ мына жерден және мына жерден оқи аласыз.

-- аудармашының пікірі --
Осымен.
10-мысалда 9 қате жіберілді.Мен оны бірден байқадым, бірақ ескерту жазуды ұмытып кетіппін. бірақ бастапқы дереккөзбен сәйкессіздік болмас үшін оны түзетпеді.

Authorы: A.Grasoff™ Дереккөзге сілтеме: Жаңадан бастаған java бағдарламашыларының қателері
Пікірлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION