Распространенные ошибки в решениях задач

Неправильное форматирование

В программе нужно правильно расставлять пробелы, отступы, переносы строк. Имеются в виду такие пробелы как, например, пробел после if перед скобкой, или пробелы в определении функции.

Неправильно: if(условие), правильно: if (условие).

Или неправильно: void main (String [] args){, правильно void main(String[] args) {.

В старые времена, когда мы встречались лично, при проверке работ я сразу расставлял пробелы правильно. Сейчас я могу сказать только, чтобы вы пользовались возможностью IDEA по форматированию кода. В меню Code выбирайте Reformat Code, обратите внимание на сочетание клавиш для этой операции Ctrl + Alt + L. И внимательно смотрите на экран при этой операции, чтобы заметить, какие именно пробелы в программе изменились.

Именование объектов

В Java есть устоявшиеся традиции именования объектов. Они помогают другим Java программистам понимать ваш код.

С большой буквы нужно называть только Классы (и Интерфейсы). Все остальные названия — с маленькой буквы. Например, не нужно называть локальные переменные с большой буквы.

Неправильно int Distance = -10. Правильно: int distance = -10.

Если в названии несколько слов, каждое следующее — с большой буквы, не разделяйте слова подчеркиваниями.

Неправильно int cat_size = -10. Правильно: int catSize = -10.

Неправильно int distance_1 = -10. Правильно: int distance1 = -10.

Ситуаций, когда в названии всё-таки нужно писать подчеркивание, очень мало, только если никак не получается выполнить предыдущие правила.

Некоторые из вас делали папки в каталоге src, хотя мы еще не успели обсудить, что это значит. Папка в src — это Java пакет. Для имен пакетов есть особые правила. Рекомендуется вообще избегать заглавных букв в названиях пакетов (папок). Для этого нужно либо избегать двух слов в названиях пакета. Не рекомендуется arts_faculty, artsFaculty. Лучше: faculty.arts (т.е. папка в папке), или можно так artsfaculty.

Не игнорируйте предупреждения

IDEA выводит предупреждения по вашему коду. Для этого она подчеркивает, подсвечивает участки кода, меняет цвет шрифта. Можно навести курсор на проблемное место, почитать, что именно в коде не так. Более заметно и явно предупреждения выводятся черточками на правой границе редактора. На них тоже можно наводить курсор и нажимать, чтобы перейти к месту с проблемой.

Не игнорируйте предупреждения. Очень часто они сигнализируют о реальной проблеме в коде — ввели переменную, забыли что-нибудь ей присвоить. Написали условие в if, а оно всегда верно, т.е. вы явно имели в виду другое условие.

Лучше всего реагировать на предупреждения прямо в процессе написания кода. Тогда вы обнаружите ошибку быстрее. Но если вы еще не очень уверенно пишете код и еще не можете отвлекаться на всё подряд, хотя бы проверяйте предупреждения в конце. Когда программа по-вашему уже работает, или не работает, и вы ищете ошибку.

Напомню, что когда курсор у вас стоит на проблемном месте в коде, слева появляется лампочка, на нее можно нажать и выбрать какое-нибудь действие для решения проблемы, если такое действие предусмотрено. Если лампочка появляется медленно или не появляется, можно нажать на нее же с помощью Alt + Enter. Еще прошу не выбирать действия до того, как вы попытались понять, о чем вас предупреждает IDEA. Иначе нет гарантии, что вы выберете адекватное решение проблемы.

Использование return

Не ставьте скобки после return, этот оператор не требует скобок.

Не рекомендуется: return (42);. Надо: return 42;.

return сразу завершает функцию, в которой он находится. Этим нужно пользоваться. Я часто встречаю код, в которой результат функции записывается в переменную и возвращается только в самом конце. Это паскаль стиль, в Java можно лучше.

Вместо

int f() {
    int x;

    if (условие)
        x = 1;
    else if (условие)
        x = 22;
    else
        x = 333;

    return x;
}

лучше пишите:

int f() {
    if (условие)
        return 1;
    else if (условие)
        return 22;
    else
        return 333;
}

Логический тип — не только для условий

Логический тип — такой же тип данных, как числовой или символьный. Его тоже можно хранить в переменных, использовать в вычислениях:

boolean f = true;
boolean g = x > 10; // например, при x=42 это вычисляется в true
boolean h = x >= 10 && x < 20; // например, при x=42 это false.

Хотя, конечно, чаще всего выражения логического типа встречаются в условиях: if (x >= 10 && x < 20) или while (i >= 0). Распространенная проблема среди студентов — считать, что операторы сравнений, логические связки можно использовать только внутри if или while. Примеры в начале раздела показывают, что это не так.

Я постоянно обращаю внимание студентки или студента на код, который не пользуется этими возможностями, но до сих пор в репозиториях он встречается:

if (x >= 10 && x < 20)
    return true;
else
    return false;

И многочисленные вариации этого кода. Лучше писать

return x >= 10 && x < 20;

Заодно можно вспомнить, что return не требует скобок, а if требует.

Кстати. IDEA показывает предупреждения для первой версии кода и предлагает его упростить.

Про скорочтение

Не все сделали задачу, но те, кто сделал, не до конца поняли идею. Суть в том, что слова должны сменяться очень быстро, при этом окажется, что текст всё равно удаётся прочитать. Поэтому задержки слов на несколько секунд не имеют смысла. Задержки должны быть всего на десятки миллисекунд.

Посмотрите на эффект в рекламе Apple Don’t blink. Текст меняется быстро, но его все равно можно комфортно читать. Правда, я не нашел русской версии, на которую не стыдно дать ссылку.

Есть только техническая тонкость, консоль IDEA не работает так быстро, как нам нужно. Такую программу лучше запускать из обычной консоли.

Модификаторы доступа

Не забывайте писать модификаторы доступа перед элементами класса. Если конструктор, то он public или private. Аналогично, методы, поля должны быть public или private, другие модификаторы мы пока не используем.

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

Несколько классов в одном файле

Мы договаривались писать по одному классу в одном файле. Один класс = один файл. Да, иногда от этого принципа отходят, но я не вижу в наших задачах причин для этого. Поэтому проследите, чтобы у вас в java файлах был только один класс.

Про класс Rational

Тип данных числителя и знаменателя

В классе Rational есть такие распространенные проблемы.

Числитель и знаменатель должны иметь тип int, в крайнем случае long, но не double. Последний нужен для хранения вещественных чисел, а по определению рациональные числа — это дроби из целых. Т.е. числитель и знаменатель точно целые.

В задаче про суммирование гармонического ряда $1+\frac12+\frac13+\frac14+\cdots$. есть проблема с 20 члена. Если числитель и знаменатель имели тип int, то после 20 члена они становятся очень большими, больше двух миллиардов, т.е. не помещаются в int. Происходит переполнение, и числитель становится отрицательным. Можно частично решить проблему, заменив int на long. Это позволит точно посчитать еще несколько членов ряда, но в какой-то момент все равно произойдет переполнение.

Следующий шаг — заменить на тип BigDecimal, он хранит целые числа произвольной длины, с ним переполнения не случится, но он работает значительно медленней int и long, он может потратить очень много памяти на хранение числителя и знаменателя, и с ним сложней писать программы, потому что не работают обычные операторы типа+, нужно вызывать функции, примерно как у нас в Rational.

Заменять на double — плохая идея. Как уже было сказано, это не подходит по смыслу, т.к. числитель и знаменатель должны быть целые. Кроме того, long и double оба занимают 8 байт, поэтому они хранят одинаковое количество разных чисел. double имеет больший диапазон, чем long за счет меньшей точности.

Конструктор

Код, который написан в конструкторе, гарантированно вызывается при создании объекта. Мы пишем в конструкторе код сокращения дроби, поэтому можно быть уверенным, что любая созданная нами дробь сокращена.

Из этого следует, что если мы напишем, например, new Rational(6, 4), то получим дробь с числителем 3 и знаменателем 2.

Я нашел несколько человек, которые перед созданием рационального числа вычисляли числитель и знаменатель, потом сокращали их на НАД, и потом вызывали конструктор, который снова делал сокращение.