Литературы, содержащей описание языка Java, сейчас достаточно много. На первой стадии знакомства с ним можно воспользоваться любым изданием, однако такие книги, как [11], [13] и [10] из библиографического списка, заведомо окажутся полезными и в дальнейшем.
В информатике и программировании изменения происходят постоянно и с большой
скоростью. Одним из самых значительных изменений за последние двадцать лет
стало появление
Выбор этого языка в качестве базового для нашего курса обусловлен прежде всего наличием у него таких качеств, как простота и мощь, безопасность, объектная ориентированность, надежность, интерактивность, архитектурная независимость, возможность интерпретации, высокая производительность и легкость в изучении. Часть из них нам понадобится с первых же шагов, другие — несколько позже.
Объектно-ориентированное программирование настолько интегрировано в Java, что написание даже простейших программ требует знания основных принципов ООП. Хотя в первой части курса мы и не собираемся акцентировать внимание на них, общее представление об основах объектно-ориентированного подхода необходимо иметь уже сейчас.
Объектно-ориентированное программирование представляет собой технологию
программирования, в основе которой лежит способность человека обобщать,
классифицировать и генерировать абстракции. Концепция ООП базируется на таких
понятиях как
Важно то, что объекты — это не только некоторые значения (данные). В них
также имеются
Произвольный объектно-ориентированный язык программирования характеризуют три основных свойства:
Более полную информацию, связанную с объектно-ориентированным программированием, можно почерпнуть из уже упоминавшейся книги [10] и замечательной, хотя и сложной для новичка, книги [2]. В нашем же курсе мы вернемся к этому вопросу в лекции 12
Перед тем, как перейти непосредственно к примерам программ, обсудим вопрос о
том, какими
Оказывается, что таких свойств достаточно много:
Так как программа на языке Java является просто записью алгоритма решения
определенной задачи, прежде всего она должна быть
Обсуждению вопросов
Свойства понятности и модифицируемости программ связаны самым тесным образом: очень трудно изменить программу, которую не понимаешь. В реальной жизни программиста задачи на модификацию уже написанных кем-то ранее программ встречаются едва ли не чаще, чем задачи на разработку программ с нуля. Решение задач на модификацию является также и весьма эффективным способом изучения как языка, так и методов программирования.
Научиться писать понятные и легко модифицируемые программы существенно легче, чем правильные и эффективные. Для этого достаточно только соблюдать несколько простых общих правил, которые мы сформулируем в применении к языку Java:
Программа на языке Java пишется в обычном текстовом файле,
содержащем в себе определения одного или нескольких классов. Имя файла обязано
совпадать с именем основного класса, определенного в нем, и иметь расширение java. Компилятор, запускаемый обычно с помощью команды , при отсутствии ошибок компиляции порождает один или
несколько выходных файлов с именами, совпадающими с именами содержащихся
в исходном файле классов, и расширением class. Для запуска
откомпилированной программы после этого необходимо выполнить команду java Filename.
В соответствии с давно установившейся традицией рассмотрим в качестве первой задачи следующую.
Задача 2.1. Напишите программу, выводящую на экран строку текста Здравствуй, мир!.
Текст программы
public class Hello {
public static void main(String[] args) {
System.out.println("Здравствуй, мир!");
}
}
Приведенный выше текст обязательно должен содержаться в файле с именем Hello.java (обратите внимание на то, что первая буква в имени является
прописной, а остальные — строчными).
Как и большинство других языков, Java допускает произвольное форматирование
текста программы. Это означает, что любую программу в принципе
можно записать в одну длинную строку или, наоборот, максимально растянуть
по вертикали, размещая на каждой строке только по одной public, class,
идентификатор Hello, разделитель {, ключевые слова public, static и void, идентификатор main,
разделитель (, идентификатор String, разделители [ и ], идентификатор args, разделители ) и {,
идентификатор ., идентификатор out,
разделитель ., идентификатор println, разделитель (,
строковый литерал "Здравствуй, мир!", разделители ), ;, и дважды }. Данная программа не содержит лексем только еще
одного типа, существующего в языке — констант, примером которой
является, скажем, число 5.
Человек, однако, не машина, и ему далеко не безразлично, как отформатирован текст программы. Неудачное форматирование существенно усложняет правильное восприятие текста и порождает потенциальные ошибки. Не обсуждая сейчас вопрос о том, какой именно стиль форматирования программ на языке Java является наиболее правильным, мы призываем читателя следовать тем образцам, которые он найдет в книге — они предлагают заведомо не самый плохой вариант.
Запомните, что в языке Java существует около шести десятков зарезервированных ключевых слов, которые имеют вполне определенное значение и не могут быть использованы для иных целей, кроме тех, для которых они предназначены.
Последовательное выполнение двух команд и java Hello должно привести к тому, что на экране появится результат
работы программы — строка Здравствуй, мир!
Мы не будем приводить подробный разбор того, каков смысл отдельных частей
рассмотренной программы, — этот комментарий можно найти в любой книге по
языку Java. Вместо этого мы обсудим решение следующей задачи, в которой будут
использованы методы уже анонсированного выше класса Xterm.
Задача 2.2.Напишите программу, печатающую на экране красивое поздравление с новым учебным годом.
Текст программы
public class NewYear {
// magic !
public static void main(String[] args) {
Xterm.clear();
Xterm.setPosition(25,8);
Xterm.print("С новым годом ", Xterm.Red);
Xterm.print("(учебным)", Xterm.Blue);
Xterm.print("!", Xterm.Red);
Xterm.setPosition(0,16);
/*
Конец программы
*/
}
}
В этой программе используются два вида комментариев из трех, существующих в
языке Java. Текст, расположенный после символов // вплоть до конца
строки, и произвольное количество строк текста между символами /* и */,
компилятором просто игнорируются. Знакомство с третьим видом комментариев,
предназначенным для автоматического
Некоторые фрагменты этой (и многих последующих) программы обсуждаться
до начала третьей главы не будут.
Только тогда, после полноценного знакомства с основными концепциями
объектно-ориентированного программирования на языке Java, можно будет
разобраться с тем, что же означает строка public static void main(String[]
args). Пока мы будем просто считать, что так надо!
Содержательная же часть приведенной программы (тело функции main, т.е.
текст, расположенный между внутренними фигурными скобками) сейчас будет
подробно разобрана. Рекомендуется откомпилировать и запустить эту программу
для того, чтобы увидеть результат ее работы, — это поможет лучше понять ее.
При этом следует иметь в виду, что кроме файла NewYear.java в данном
случае необходим еще и файл Xterm.java (его содержимое приведено
для справки в последней секции данной лекции).
Объект, с которым ведется работа в программе, — Xterm. Подробно
он рассматривается чуть ниже, а пока
отметим только то, что он определяет терминал, обеспечивающий ввод
чисел и вывод строк текста.
Объект Xterm содержит в себе некоторую информацию
или, как говорят находится в некотором
На самом деле в классе Xterm нет компонент, содержащих указанную
информацию (она хранится совсем в другом объекте), однако подобное упрощение
реальной ситуации является весьма удобным.
Множество всех состояний, в которых может находиться объект, называется
В языке Java для вызова метода необходимо сначала указать объект,
к которому применяется тот или иной метод, а затем после точки записать имя
метода. Именно этот синтаксис, в котором главная роль отводится
Некоторые из методов требуют для своего выполнения указания
дополнительных объектов. Такие дополнительные объекты называют
Рассматриваемая программа содержит вызов трех различных методов класса Xterm: clear, setPosition и print. Первый из них
очищает окно терминала и не имеет параметров, второй перемещает курсор
в позицию, задаваемую параметрами метода, а третий позволяет вывести
строку текста. При этом первый параметр метода print определяет
выводимую строку, а второй задает цвет символов.
Все встретившиеся в данной программе методы не возвращают значений, однако
это вовсе не является обязательным. Метод может возвращать в качестве
результата своей работы объект произвольного типа с помощью оператора return.
Типы данных в языке Java подразделяются на boolean, символьного char, целых типов byte, short, int и long, и типов для представления
действительных чисел float и double. Ссылочные типы позволяют
работать с объектами и массивами.
Множество всех объектов с одинаковым пространством состояний и одинаковым
набором методов называется
Для работы с объектами, массивами и сущностями простых типов используются =.
В языке Java все переменные должны быть
тип идентификатор [= значение] [, идентификатор [= значение ]...];
Описание переменной определяет ее тип и имя (
Квадратные скобки в приведенной записи означают фрагменты, которые могут быть опущены. В частности, переменная может быть объявлена без присваивания ей начального значения (инициализации). В языке Java все такие переменные автоматически инициализируются нулем.
Java — int и double операция сложения
определена, а для типа boolean — нет.
Из приведенного правила есть исключения. В языке Java производится g при этом произойдет преобразование целого
числа один в действительное (что именно это значит, описано в следующем
параграфе):
double f = 1.0, g = 1;
Обратное преобразование (из действительного числа в целое) в языке Java
автоматически произойти не может, так как это может повлечь за собой
искажение исходного значения. В тех ситуациях, когда действительно необходимо
выполнить подобную операцию (найти целую часть числа), необходимо
воспользоваться
double f = 1.1;
int n = (int) f, m = (int) (f + 0.8);
Для преобразования типа переменной или какого-то выражения необходимо
перед ним указать в круглых скобках новый тип. В приведенном выше примере
обе целочисленные переменные ( m и n ) окажутся равными единице.
Кроме уже встретившихся операторов вызова метода, присваивания и
преобразования
типа в языка Java определен целый ряд других. Операторы бывают
Существует и другая классификация операторов: они делятся на
Методы clear и setPosition этого класса были полностью описаны
ранее, а вот об уже встречавшемся методе print было рассказано далеко
не все. Начнем с того, что в классе Xterm имеется целых три метода с
именем print: с одним, двумя и тремя аргументами. Первый аргумент —
выводимая строка, второй (в случае его наличия) определяет цвет символов, а
третий (если он есть) — цвет фона.
Второй и третий аргументы являются просто целыми числами, которым для удобства использования присвоены символические имена:
public static final int Black = 0;
public static final int Red = 1;
public static final int Green = 2;
public static final int Yellow = 3;
public static final int Blue = 4;
public static final int Magenta = 5;
public static final int Cyan = 6;
public static final int White = 7;
Класс Xterm дает возможность выводить только строки, но язык Java
позволяет легко преобразовывать данные всех простых типов в строковое
представление. Самым простым способом является использование оператора + с первым операндом, являющемся строкой. Оператор + является
2 + 2 // 4
"i = " + "c" // "i = c"
"i = " + 2 // "i = 2"
"x = " + (3./2.) // "x = 1.5"
"" + (3./2.) // "1.5"
2 + "i = " // Ошибка!
Используя этот оператор и метод print класса Xterm, можно
выводить значения переменных любого из простых типов. Полезно знать, что "" обозначает пустую строку, а печать "\n" приводит к тому,
что курсор терминала перемещается в начало следующей строки.
Для того чтобы не добавлять постоянно к выводимой строке "\n", можно
пользоваться методами println, действие которых в остальном совершенно
аналогично работе методов print.
Оставшиеся неразобранными методы класса Xterm предназначены для ввода
целых и действительных чисел. Они позволяют работать с величинами типов int, long, float и double. Их имена вполне естественны: inputInt, inputLong, inputFloat и inputDouble
соответственно. Все эти методы возвращают в качестве результата введенное
число, если только в процессе ввода не произошла какая-либо ошибка. При этом
предполагается, что за один раз может быть введено только одно число, и ввод
завершается нажатием на клавишу Enter.
Аналогично работает и метод inputChars, позволяющий осуществить ввод
строки символьной информации, размещая ее в массиве символов.
public static void main(String[] args)
следует заменить на
public static void main(String[] args) throws Exception
Все четыре метода ввода чисел и метод ввода строки символов, определенные в
классе Xterm, являются Xterm позволяют указывать в качестве аргумента строку, которая
будет выведена в качестве подсказки. Это весьма удобно, так
как позволяет при выполнении программы явно увидеть, когда именно
следует вводить ту или иную информацию.
Использование класса Xterm для организации операций ввода/вывода
будет проиллюстрировано при решении следующей задачи.
Задача 2.3. Напишите программу, вводящую два целых числа a и b, печатающую
их, затем обменивающую значения этих переменных (так, чтобы новое значение a стало равно старому значению b, и наоборот) и вновь их
печатающую.
Текст программы
public class Change {
public static void main(String[] args) throws Exception {
int a = Xterm.inputInt("Введите первое число -> ");
int b = Xterm.inputInt("Введите второе число -> ");
Xterm.println("До обмена: a = " + a + "; b = " + b);
int c = a;
a = b;
b = c;
Xterm.println("После обмена: a = " + a + "; b = " + b);
}
}
Эта программа использует третью переменную c для того, чтобы
сохранить
в ней начальное значение переменной a, которое иначе оказалось бы
утерянным при выполнении оператора присваивания a = b;. Попробуйте
придумать какой-либо способ обменять значения двух переменных без введения
третьей.
Все программы, которые мы писали до сих пор, были
Простейшими конструкциями, предназначенными для изменения порядка выполнения
операторов, являются if, if-else и switch. Применение первых двух из них требует использования логических
выражений и логических операторов, к рассмотрению которых мы сейчас и
перейдем.
Подробному изложению основ теории ==, а второй — с помощью символов !=. Стандартные математические обозначения для них — это $$=$$ и $$\ne$$
соответственно.
Следует помнить, что при использовании этих операторов для величин ссылочных
типов, проверяется факт равенства (или неравенства)
Объекты числовых типов можно сравнивать между собой также и с помощью
таких операторов, как < (меньше), <= (меньше или равно), > (больше) и >= (больше или равно), математическими обозначениями
для которых являются $$<$$, $$\leqslant$$, $$>$$ и $$\geqslant$$.
Из простейших логических выражений, к которым относятся логические
переменные
и результаты сравнений, можно конструировать более сложные, используя
следующие логические операторы:
В математической теории исчисления предикатов отрицание принято обозначать
символом $$\lnot$$ (или просто $$!$$), операторам
логического Или и И
соответствуют
Отрицание логического выражения, имеющего значение $$F$$ ( Ложь ), есть $$T$$ ( Истина ), и наоборот. Дизъюнкция истинна, если истинен хотя бы один из ее аргументов, а конъюнкция — только при истинности обоих. Исключающее Или истинно тогда и только тогда, когда истинен ровно один из аргументов, а назначение и истинность остальных логических операторов будут описаны чуть позже.
Управляющая конструкция if-else в зависимости от значения логического
выражения позволяет выполнять различные части программного кода. В общей форме
этот оператор записывается следующим образом:
if (логическое_выражение) блок1; [ else блок2; ]
Если условие, задаваемое заключенным в круглые скобки логическим выражением
истинно, то будет выполняться блок1, иначе — блок2. Часть else
может и отсутствовать.
Управляющие конструкции if и if-else могут быть вложенными, и
с помощью
Эту же задачу часто удобнее решать с помощью оператора switch,
общий вид которого таков:
switch (выражение) {
case значение1:
блок 1;
break;
case значение2:
блок 2;
break;
...
case значениеN:
блокN;
break;
default:
блок N+1;
}
Выражение, которое должно иметь целочисленный тип, сравнивается со всеми
значениями (тоже целочисленными), указанными после ключевых слов case.
Если оно оказывается совпадающим с одним из них, то управление передается
соответствующему блоку операторов, а если совпадения не обнаруживается, то
управление передается блоку default (если таковой существует, ибо он
не является обязательным). После выполнения того блока, на который было
передано управление, оператор break вызывает завершение выполнения
оператора switch. При отсутствии оператора break управление просто
будет передано следующему блоку за только что выполненным.
Рассмотрим использование описанных операторов на примере решения следующих несложных задач.
Задача 2.4.Напишите программу, вводящую три целых числа, и печатающую максимальное из них.
Текст программы
public class MaxVal3 {
public static void main(String[] args) throws Exception {
int a = Xterm.inputInt("Введите первое число -> ");
int b = Xterm.inputInt("Введите второе число -> ");
int c = Xterm.inputInt("Введите третье число -> ");
int max;
if (a > b) max = a;
else max = b;
if (c > max) max = c;
Xterm.println("Максимальное число из введенных = "+max);
}
}
В этой программе переменной max сначала присваивается максимальное
значение из двух чисел — a и b, а затем, если третье число c больше этой величины, переменной max присваивается его
значение.
Использование вложенных управляющих конструкций иллюстрирует программа, решающая следующую задачу.
Задача 2.5.Напишите программу, вводящую три целых числа, и печатающую количество максимальных среди введенных чисел.
Для экономии места приведем только содержательную часть решения этой задачи.
Фрагмент программы (NumMaxVal3v1.java)
int nMax;
if (a == b) {
if (a == c) nMax = 3;
else {
if (a > c) nMax = 2;
else nMax = 1;
}
} else {
if (a > b) {
if (a == c) nMax = 2;
else nMax = 1;
} else {
if (b == c) nMax = 2;
else nMax = 1;
}
}
Xterm.println("Количество максимальных чисел = "
+ nMax);
Приведенная программа является достаточно громоздкой. Гораздо понятнее следующее решение той же задачи.
Фрагмент программы (NumMaxVal3v2.java)
if (a > b) max = a;
else max = b;
if (c > max) max = c;
if (a == max) nMax += 1;
if (b == max) nMax += 1;
if (c == max) nMax += 1;
Xterm.println("Количество максимальных чисел = "
+ nMax);
Здесь мы сначала определяем максимальное по величине число, а затем просто считаем количество равных этому значению величин. Следующая задача является более простой, но она позволит нам проиллюстрировать работу с логическими операторами.
Задача 2.6. Напишите программу, вводящую три целых числа, и печатающую Yes в том
случае, если среди введенных чисел есть одинаковые, и No — иначе.
Текст программы
public class Equal3v1 {
public static void main(String[] args) throws Exception {
int a = Xterm.inputInt("Введите первое число -> ");
int b = Xterm.inputInt("Введите второе число -> ");
int c = Xterm.inputInt("Введите третье число -> ");
if ( (a == b) || (a == c) || (b == c) )
Xterm.println("Yes");
else
Xterm.println("No");
}
}
Обратите внимание, что в данной программе использованы операторы условного Или ||, а не логического Или |. Это вполне типично —
операторы логического Или | и логического И
на практике не используют — вместо них применяют условные операторы ||
и .
Как это следует из определений, если первый операнд дизъюнкции истинен, то
независимо от значения второго операнда результатом будет истина. Аналогично
в случае конъюнкции при ложном первом операнде значение второго операнда на
результат не влияет — он всегда будет ложным. При выполнении условных
операторов || и исполняющая система Java не производит оценку
второго операнда логического выражения, если результат ясен из значения
первого операнда. Иногда это просто ускоряет вычисления, а иногда позволяет
добиться и большего, как, например, в следующем программном фрагменте.
if (a==0 || b/a > 0) x = y;
При $$a=0$$ второй операнд оператора || вычисляться не будет
и деления
на ноль не произойдет, как это было бы в случае использования логического
оператора Или |.
Можно изменить проверяемое условие среди чисел есть равные в рассмотренной выше программе на его отрицание среди чисел нет равных и переписать программу, заменяя оператор Или на И.
Фрагмент программы (Equal3v2.java)
if ( (a != b) (a != c) (b != c) )
Xterm.println("No");
else
Xterm.println("Yes");
При решении рассматриваемой задачи можно обойтись без условных операторов, если вспомнить, что произведение чисел равно нулю тогда и только тогда, когда одно из них нулевое. Это позволяет написать следующую программу.
Фрагмент программы (Equal3v3.java)
if ((a-b)*(b-c)*(c-a) != 0)
Xterm.println("No");
else
Xterm.println("Yes");
И, наконец, заметим, что программа запишется короче, если заменить в ней
оператор if-else на ?:, общая форма
записи которого имеет следующий вид:
выражение1 ? выражение2 : выражение3
Если результат вычисления первого выражения истинен, то выполняется выражение2 (второй операнд), а иначе — выражение3 (третий операнд). При использовании этой конструкции два последних ее выражения должны иметь один и тот же тип, в данном случае — строковый.
Фрагмент программы (Equal3v4.java)
Xterm.println( (a-b)*(b-c)*(c-a) != 0 ? "No" : "Yes" );
Задача 2.7.Напишите программу, вводящую два целых числа a и b, печатающую
их, затем обменивающую значения этих переменных (так, чтобы новое значение a стало равно старому значению b, и наоборот) и вновь их
печатающую, которая не использовала бы иных переменных, кроме $$a$$ и $$b$$.
Задача 2.8.Напишите программу, вводящую три целых числа, и печатающую второе по
величине, если оно существует, и No — иначе.
Задача 2.9.Напишите программу, вводящую действительное число, которая рассматривает это число, как координаты точки на прямой, и печатает расстояние от этой точки до отрезка $$[0,1]$$.
Задача 2.10.Напишите программу, вводящую три целых числа, и печатающую с использованием
всех возможностей класса Xterm как сами числа, так и их среднее
арифметическое.
Задача 2.11.Напишите программу, вводящую действительные коэффициенты $$a$$, $$b$$ и $$c$$ квадратного уравнения $$a x^2 + b x + c = 0$$ с положительным дискриминантом, находящую оба корня этого уравнения.
Эта секция содержит для справки программную реализацию класса Xterm,
который мы будем активно использовать всю первую половину нашего курса.
import java.io.*;
// Класс, обеспечивающий вывод строк текста с возможностью
// позиционирования и использования цветов, а также ввод чисел
// целых типов int и long и вещественных float и double.
public class Xterm {
private static final DataInputStream in =
new DataInputStream(System.in);
private static final int MAXLEN = 255;
private static String inputString() throws IOException {
byte buf[] = new byte[MAXLEN];
int i = in.read(buf);
return new String(buf,0,i-1);
}
// Имена цветов символов и фона
public static final int Black = 0;
public static final int Red = 1;
public static final int Green = 2;
public static final int Yellow = 3;
public static final int Blue = 4;
public static final int Magenta = 5;
public static final int Cyan = 6;
public static final int White = 7;
// Метод очистки экрана
public static void clear() {
System.out.print("\033[2J");
}
// Метод позиционирования курсора
public static void setPosition(int x, int y) {
System.out.print("\033[" + (y+1) + ";" + (x+1) + "H");
}
// Методы вывода строки
public static void print(String txt) {
System.out.print("\033[0m\033[30;1m"+txt+"\033[0m\033[30m");
}
public static void print(String txt, int fg) {
System.out.print("\033[0m\033[" + (30+fg)
+";1m" + txt + "\033[0m\033[30m");
}
public static void print(String txt, int fg, int bg) {
System.out.print("\033[0m\033["+(bg==7?"":""+(40+bg)+";")+
(30+fg)+";1m" + txt + "\033[0m\033[30m");
}
public static void println(String txt) {
print(txt + "\n");
}
public static void println(String txt, int fg) {
print(txt + "\n");
}
public static void println(String txt, int fg, int bg) {
print(txt + "\n");
}
// Методы ввода чисел типов int, long, float, double
public static int inputInt() throws IOException,
NumberFormatException {
return Integer.valueOf(inputString()).intValue();
}
public static int inputInt(String prompt) throws IOException,
NumberFormatException {
print(prompt); return inputInt();
}
public static long inputLong() throws IOException,
NumberFormatException {
return Long.valueOf(inputString()).longValue();
}
public static long inputLong(String prompt) throws IOException,
NumberFormatException {
print(prompt); return inputLong();
}
public static float inputFloat() throws IOException,
NumberFormatException {
return Float.valueOf(inputString()).floatValue();
}
public static float inputFloat(String prompt) throws IOException,
NumberFormatException {
print(prompt); return inputFloat();
}
public static double inputDouble() throws IOException,
NumberFormatException {
return Double.valueOf(inputString()).doubleValue();
}
public static double inputDouble(String prompt)
throws IOException, NumberFormatException {
print(prompt); return inputDouble();
}
// Методы ввода строки, рассматриваемой как массив символов.
public static char[] inputChars() throws IOException {
return (inputString()).toCharArray();
}
public static char[] inputChars(String prompt)
throws IOException {
print(prompt);
return (inputString()).toCharArray();
}
}
Литературы, содержащей описание языка Java, сейчас достаточно много. На первой стадии знакомства с ним можно воспользоваться любым изданием, однако такие книги, как [11], [13] и [10] из библиографического списка, заведомо окажутся полезными и в дальнейшем.
В информатике и программировании изменения происходят постоянно и с большой
скоростью. Одним из самых значительных изменений за последние двадцать лет
стало появление
Выбор этого языка в качестве базового для нашего курса обусловлен прежде всего наличием у него таких качеств, как простота и мощь, безопасность, объектная ориентированность, надежность, интерактивность, архитектурная независимость, возможность интерпретации, высокая производительность и легкость в изучении. Часть из них нам понадобится с первых же шагов, другие — несколько позже.
Объектно-ориентированное программирование настолько интегрировано в Java, что написание даже простейших программ требует знания основных принципов ООП. Хотя в первой части курса мы и не собираемся акцентировать внимание на них, общее представление об основах объектно-ориентированного подхода необходимо иметь уже сейчас.
Объектно-ориентированное программирование представляет собой технологию
программирования, в основе которой лежит способность человека обобщать,
классифицировать и генерировать абстракции. Концепция ООП базируется на таких
понятиях как
Важно то, что объекты — это не только некоторые значения (данные). В них
также имеются
Произвольный объектно-ориентированный язык программирования характеризуют три основных свойства:
Более полную информацию, связанную с объектно-ориентированным программированием, можно почерпнуть из уже упоминавшейся книги [10] и замечательной, хотя и сложной для новичка, книги [2]. В нашем же курсе мы вернемся к этому вопросу в лекции 12
Перед тем, как перейти непосредственно к примерам программ, обсудим вопрос о
том, какими
Оказывается, что таких свойств достаточно много:
Так как программа на языке Java является просто записью алгоритма решения
определенной задачи, прежде всего она должна быть
Обсуждению вопросов
Свойства понятности и модифицируемости программ связаны самым тесным образом: очень трудно изменить программу, которую не понимаешь. В реальной жизни программиста задачи на модификацию уже написанных кем-то ранее программ встречаются едва ли не чаще, чем задачи на разработку программ с нуля. Решение задач на модификацию является также и весьма эффективным способом изучения как языка, так и методов программирования.
Научиться писать понятные и легко модифицируемые программы существенно легче, чем правильные и эффективные. Для этого достаточно только соблюдать несколько простых общих правил, которые мы сформулируем в применении к языку Java:
Программа на языке Java пишется в обычном текстовом файле,
содержащем в себе определения одного или нескольких классов. Имя файла обязано
совпадать с именем основного класса, определенного в нем, и иметь расширение java. Компилятор, запускаемый обычно с помощью команды , при отсутствии ошибок компиляции порождает один или
несколько выходных файлов с именами, совпадающими с именами содержащихся
в исходном файле классов, и расширением class. Для запуска
откомпилированной программы после этого необходимо выполнить команду java Filename.
В соответствии с давно установившейся традицией рассмотрим в качестве первой задачи следующую.
Задача 2.1. Напишите программу, выводящую на экран строку текста Здравствуй, мир!.
Текст программы
public class Hello {
public static void main(String[] args) {
System.out.println("Здравствуй, мир!");
}
}
Приведенный выше текст обязательно должен содержаться в файле с именем Hello.java (обратите внимание на то, что первая буква в имени является
прописной, а остальные — строчными).
Как и большинство других языков, Java допускает произвольное форматирование
текста программы. Это означает, что любую программу в принципе
можно записать в одну длинную строку или, наоборот, максимально растянуть
по вертикали, размещая на каждой строке только по одной public, class,
идентификатор Hello, разделитель {, ключевые слова public, static и void, идентификатор main,
разделитель (, идентификатор String, разделители [ и ], идентификатор args, разделители ) и {,
идентификатор ., идентификатор out,
разделитель ., идентификатор println, разделитель (,
строковый литерал "Здравствуй, мир!", разделители ), ;, и дважды }. Данная программа не содержит лексем только еще
одного типа, существующего в языке — констант, примером которой
является, скажем, число 5.
Человек, однако, не машина, и ему далеко не безразлично, как отформатирован текст программы. Неудачное форматирование существенно усложняет правильное восприятие текста и порождает потенциальные ошибки. Не обсуждая сейчас вопрос о том, какой именно стиль форматирования программ на языке Java является наиболее правильным, мы призываем читателя следовать тем образцам, которые он найдет в книге — они предлагают заведомо не самый плохой вариант.
Запомните, что в языке Java существует около шести десятков зарезервированных ключевых слов, которые имеют вполне определенное значение и не могут быть использованы для иных целей, кроме тех, для которых они предназначены.
Последовательное выполнение двух команд и java Hello должно привести к тому, что на экране появится результат
работы программы — строка Здравствуй, мир!
Мы не будем приводить подробный разбор того, каков смысл отдельных частей
рассмотренной программы, — этот комментарий можно найти в любой книге по
языку Java. Вместо этого мы обсудим решение следующей задачи, в которой будут
использованы методы уже анонсированного выше класса Xterm.
Задача 2.2.Напишите программу, печатающую на экране красивое поздравление с новым учебным годом.
Текст программы
public class NewYear {
// magic !
public static void main(String[] args) {
Xterm.clear();
Xterm.setPosition(25,8);
Xterm.print("С новым годом ", Xterm.Red);
Xterm.print("(учебным)", Xterm.Blue);
Xterm.print("!", Xterm.Red);
Xterm.setPosition(0,16);
/*
Конец программы
*/
}
}
В этой программе используются два вида комментариев из трех, существующих в
языке Java. Текст, расположенный после символов // вплоть до конца
строки, и произвольное количество строк текста между символами /* и */,
компилятором просто игнорируются. Знакомство с третьим видом комментариев,
предназначенным для автоматического
Некоторые фрагменты этой (и многих последующих) программы обсуждаться
до начала третьей главы не будут.
Только тогда, после полноценного знакомства с основными концепциями
объектно-ориентированного программирования на языке Java, можно будет
разобраться с тем, что же означает строка public static void main(String[]
args). Пока мы будем просто считать, что так надо!
Содержательная же часть приведенной программы (тело функции main, т.е.
текст, расположенный между внутренними фигурными скобками) сейчас будет
подробно разобрана. Рекомендуется откомпилировать и запустить эту программу
для того, чтобы увидеть результат ее работы, — это поможет лучше понять ее.
При этом следует иметь в виду, что кроме файла NewYear.java в данном
случае необходим еще и файл Xterm.java (его содержимое приведено
для справки в последней секции данной лекции).
Объект, с которым ведется работа в программе, — Xterm. Подробно
он рассматривается чуть ниже, а пока
отметим только то, что он определяет терминал, обеспечивающий ввод
чисел и вывод строк текста.
Объект Xterm содержит в себе некоторую информацию
или, как говорят находится в некотором
На самом деле в классе Xterm нет компонент, содержащих указанную
информацию (она хранится совсем в другом объекте), однако подобное упрощение
реальной ситуации является весьма удобным.
Множество всех состояний, в которых может находиться объект, называется
В языке Java для вызова метода необходимо сначала указать объект,
к которому применяется тот или иной метод, а затем после точки записать имя
метода. Именно этот синтаксис, в котором главная роль отводится
Некоторые из методов требуют для своего выполнения указания
дополнительных объектов. Такие дополнительные объекты называют
Рассматриваемая программа содержит вызов трех различных методов класса Xterm: clear, setPosition и print. Первый из них
очищает окно терминала и не имеет параметров, второй перемещает курсор
в позицию, задаваемую параметрами метода, а третий позволяет вывести
строку текста. При этом первый параметр метода print определяет
выводимую строку, а второй задает цвет символов.
Все встретившиеся в данной программе методы не возвращают значений, однако
это вовсе не является обязательным. Метод может возвращать в качестве
результата своей работы объект произвольного типа с помощью оператора return.
Типы данных в языке Java подразделяются на boolean, символьного char, целых типов byte, short, int и long, и типов для представления
действительных чисел float и double. Ссылочные типы позволяют
работать с объектами и массивами.
Множество всех объектов с одинаковым пространством состояний и одинаковым
набором методов называется
Для работы с объектами, массивами и сущностями простых типов используются =.
В языке Java все переменные должны быть
тип идентификатор [= значение] [, идентификатор [= значение ]...];
Описание переменной определяет ее тип и имя (
Квадратные скобки в приведенной записи означают фрагменты, которые могут быть опущены. В частности, переменная может быть объявлена без присваивания ей начального значения (инициализации). В языке Java все такие переменные автоматически инициализируются нулем.
Java — int и double операция сложения
определена, а для типа boolean — нет.
Из приведенного правила есть исключения. В языке Java производится g при этом произойдет преобразование целого
числа один в действительное (что именно это значит, описано в следующем
параграфе):
double f = 1.0, g = 1;
Обратное преобразование (из действительного числа в целое) в языке Java
автоматически произойти не может, так как это может повлечь за собой
искажение исходного значения. В тех ситуациях, когда действительно необходимо
выполнить подобную операцию (найти целую часть числа), необходимо
воспользоваться
double f = 1.1;
int n = (int) f, m = (int) (f + 0.8);
Для преобразования типа переменной или какого-то выражения необходимо
перед ним указать в круглых скобках новый тип. В приведенном выше примере
обе целочисленные переменные ( m и n ) окажутся равными единице.
Кроме уже встретившихся операторов вызова метода, присваивания и
преобразования
типа в языка Java определен целый ряд других. Операторы бывают
Существует и другая классификация операторов: они делятся на
Методы clear и setPosition этого класса были полностью описаны
ранее, а вот об уже встречавшемся методе print было рассказано далеко
не все. Начнем с того, что в классе Xterm имеется целых три метода с
именем print: с одним, двумя и тремя аргументами. Первый аргумент —
выводимая строка, второй (в случае его наличия) определяет цвет символов, а
третий (если он есть) — цвет фона.
Второй и третий аргументы являются просто целыми числами, которым для удобства использования присвоены символические имена:
public static final int Black = 0;
public static final int Red = 1;
public static final int Green = 2;
public static final int Yellow = 3;
public static final int Blue = 4;
public static final int Magenta = 5;
public static final int Cyan = 6;
public static final int White = 7;
Класс Xterm дает возможность выводить только строки, но язык Java
позволяет легко преобразовывать данные всех простых типов в строковое
представление. Самым простым способом является использование оператора + с первым операндом, являющемся строкой. Оператор + является
2 + 2 // 4
"i = " + "c" // "i = c"
"i = " + 2 // "i = 2"
"x = " + (3./2.) // "x = 1.5"
"" + (3./2.) // "1.5"
2 + "i = " // Ошибка!
Используя этот оператор и метод print класса Xterm, можно
выводить значения переменных любого из простых типов. Полезно знать, что "" обозначает пустую строку, а печать "\n" приводит к тому,
что курсор терминала перемещается в начало следующей строки.
Для того чтобы не добавлять постоянно к выводимой строке "\n", можно
пользоваться методами println, действие которых в остальном совершенно
аналогично работе методов print.
Оставшиеся неразобранными методы класса Xterm предназначены для ввода
целых и действительных чисел. Они позволяют работать с величинами типов int, long, float и double. Их имена вполне естественны: inputInt, inputLong, inputFloat и inputDouble
соответственно. Все эти методы возвращают в качестве результата введенное
число, если только в процессе ввода не произошла какая-либо ошибка. При этом
предполагается, что за один раз может быть введено только одно число, и ввод
завершается нажатием на клавишу Enter.
Аналогично работает и метод inputChars, позволяющий осуществить ввод
строки символьной информации, размещая ее в массиве символов.
public static void main(String[] args)
следует заменить на
public static void main(String[] args) throws Exception
Все четыре метода ввода чисел и метод ввода строки символов, определенные в
классе Xterm, являются Xterm позволяют указывать в качестве аргумента строку, которая
будет выведена в качестве подсказки. Это весьма удобно, так
как позволяет при выполнении программы явно увидеть, когда именно
следует вводить ту или иную информацию.
Использование класса Xterm для организации операций ввода/вывода
будет проиллюстрировано при решении следующей задачи.
Задача 2.3. Напишите программу, вводящую два целых числа a и b, печатающую
их, затем обменивающую значения этих переменных (так, чтобы новое значение a стало равно старому значению b, и наоборот) и вновь их
печатающую.
Текст программы
public class Change {
public static void main(String[] args) throws Exception {
int a = Xterm.inputInt("Введите первое число -> ");
int b = Xterm.inputInt("Введите второе число -> ");
Xterm.println("До обмена: a = " + a + "; b = " + b);
int c = a;
a = b;
b = c;
Xterm.println("После обмена: a = " + a + "; b = " + b);
}
}
Эта программа использует третью переменную c для того, чтобы
сохранить
в ней начальное значение переменной a, которое иначе оказалось бы
утерянным при выполнении оператора присваивания a = b;. Попробуйте
придумать какой-либо способ обменять значения двух переменных без введения
третьей.
Все программы, которые мы писали до сих пор, были
Простейшими конструкциями, предназначенными для изменения порядка выполнения
операторов, являются if, if-else и switch. Применение первых двух из них требует использования логических
выражений и логических операторов, к рассмотрению которых мы сейчас и
перейдем.
Подробному изложению основ теории ==, а второй — с помощью символов !=. Стандартные математические обозначения для них — это $$=$$ и $$\ne$$
соответственно.
Следует помнить, что при использовании этих операторов для величин ссылочных
типов, проверяется факт равенства (или неравенства)
Объекты числовых типов можно сравнивать между собой также и с помощью
таких операторов, как < (меньше), <= (меньше или равно), > (больше) и >= (больше или равно), математическими обозначениями
для которых являются $$<$$, $$\leqslant$$, $$>$$ и $$\geqslant$$.
Из простейших логических выражений, к которым относятся логические
переменные
и результаты сравнений, можно конструировать более сложные, используя
следующие логические операторы:
В математической теории исчисления предикатов отрицание принято обозначать
символом $$\lnot$$ (или просто $$!$$), операторам
логического Или и И
соответствуют
Отрицание логического выражения, имеющего значение $$F$$ ( Ложь ), есть $$T$$ ( Истина ), и наоборот. Дизъюнкция истинна, если истинен хотя бы один из ее аргументов, а конъюнкция — только при истинности обоих. Исключающее Или истинно тогда и только тогда, когда истинен ровно один из аргументов, а назначение и истинность остальных логических операторов будут описаны чуть позже.
Управляющая конструкция if-else в зависимости от значения логического
выражения позволяет выполнять различные части программного кода. В общей форме
этот оператор записывается следующим образом:
if (логическое_выражение) блок1; [ else блок2; ]
Если условие, задаваемое заключенным в круглые скобки логическим выражением
истинно, то будет выполняться блок1, иначе — блок2. Часть else
может и отсутствовать.
Управляющие конструкции if и if-else могут быть вложенными, и
с помощью
Эту же задачу часто удобнее решать с помощью оператора switch,
общий вид которого таков:
switch (выражение) {
case значение1:
блок 1;
break;
case значение2:
блок 2;
break;
...
case значениеN:
блокN;
break;
default:
блок N+1;
}
Выражение, которое должно иметь целочисленный тип, сравнивается со всеми
значениями (тоже целочисленными), указанными после ключевых слов case.
Если оно оказывается совпадающим с одним из них, то управление передается
соответствующему блоку операторов, а если совпадения не обнаруживается, то
управление передается блоку default (если таковой существует, ибо он
не является обязательным). После выполнения того блока, на который было
передано управление, оператор break вызывает завершение выполнения
оператора switch. При отсутствии оператора break управление просто
будет передано следующему блоку за только что выполненным.
Рассмотрим использование описанных операторов на примере решения следующих несложных задач.
Задача 2.4.Напишите программу, вводящую три целых числа, и печатающую максимальное из них.
Текст программы
public class MaxVal3 {
public static void main(String[] args) throws Exception {
int a = Xterm.inputInt("Введите первое число -> ");
int b = Xterm.inputInt("Введите второе число -> ");
int c = Xterm.inputInt("Введите третье число -> ");
int max;
if (a > b) max = a;
else max = b;
if (c > max) max = c;
Xterm.println("Максимальное число из введенных = "+max);
}
}
В этой программе переменной max сначала присваивается максимальное
значение из двух чисел — a и b, а затем, если третье число c больше этой величины, переменной max присваивается его
значение.
Использование вложенных управляющих конструкций иллюстрирует программа, решающая следующую задачу.
Задача 2.5.Напишите программу, вводящую три целых числа, и печатающую количество максимальных среди введенных чисел.
Для экономии места приведем только содержательную часть решения этой задачи.
Фрагмент программы (NumMaxVal3v1.java)
int nMax;
if (a == b) {
if (a == c) nMax = 3;
else {
if (a > c) nMax = 2;
else nMax = 1;
}
} else {
if (a > b) {
if (a == c) nMax = 2;
else nMax = 1;
} else {
if (b == c) nMax = 2;
else nMax = 1;
}
}
Xterm.println("Количество максимальных чисел = "
+ nMax);
Приведенная программа является достаточно громоздкой. Гораздо понятнее следующее решение той же задачи.
Фрагмент программы (NumMaxVal3v2.java)
if (a > b) max = a;
else max = b;
if (c > max) max = c;
if (a == max) nMax += 1;
if (b == max) nMax += 1;
if (c == max) nMax += 1;
Xterm.println("Количество максимальных чисел = "
+ nMax);
Здесь мы сначала определяем максимальное по величине число, а затем просто считаем количество равных этому значению величин. Следующая задача является более простой, но она позволит нам проиллюстрировать работу с логическими операторами.
Задача 2.6. Напишите программу, вводящую три целых числа, и печатающую Yes в том
случае, если среди введенных чисел есть одинаковые, и No — иначе.
Текст программы
public class Equal3v1 {
public static void main(String[] args) throws Exception {
int a = Xterm.inputInt("Введите первое число -> ");
int b = Xterm.inputInt("Введите второе число -> ");
int c = Xterm.inputInt("Введите третье число -> ");
if ( (a == b) || (a == c) || (b == c) )
Xterm.println("Yes");
else
Xterm.println("No");
}
}
Обратите внимание, что в данной программе использованы операторы условного Или ||, а не логического Или |. Это вполне типично —
операторы логического Или | и логического И
на практике не используют — вместо них применяют условные операторы ||
и .
Как это следует из определений, если первый операнд дизъюнкции истинен, то
независимо от значения второго операнда результатом будет истина. Аналогично
в случае конъюнкции при ложном первом операнде значение второго операнда на
результат не влияет — он всегда будет ложным. При выполнении условных
операторов || и исполняющая система Java не производит оценку
второго операнда логического выражения, если результат ясен из значения
первого операнда. Иногда это просто ускоряет вычисления, а иногда позволяет
добиться и большего, как, например, в следующем программном фрагменте.
if (a==0 || b/a > 0) x = y;
При $$a=0$$ второй операнд оператора || вычисляться не будет
и деления
на ноль не произойдет, как это было бы в случае использования логического
оператора Или |.
Можно изменить проверяемое условие среди чисел есть равные в рассмотренной выше программе на его отрицание среди чисел нет равных и переписать программу, заменяя оператор Или на И.
Фрагмент программы (Equal3v2.java)
if ( (a != b) (a != c) (b != c) )
Xterm.println("No");
else
Xterm.println("Yes");
При решении рассматриваемой задачи можно обойтись без условных операторов, если вспомнить, что произведение чисел равно нулю тогда и только тогда, когда одно из них нулевое. Это позволяет написать следующую программу.
Фрагмент программы (Equal3v3.java)
if ((a-b)*(b-c)*(c-a) != 0)
Xterm.println("No");
else
Xterm.println("Yes");
И, наконец, заметим, что программа запишется короче, если заменить в ней
оператор if-else на ?:, общая форма
записи которого имеет следующий вид:
выражение1 ? выражение2 : выражение3
Если результат вычисления первого выражения истинен, то выполняется выражение2 (второй операнд), а иначе — выражение3 (третий операнд). При использовании этой конструкции два последних ее выражения должны иметь один и тот же тип, в данном случае — строковый.
Фрагмент программы (Equal3v4.java)
Xterm.println( (a-b)*(b-c)*(c-a) != 0 ? "No" : "Yes" );
Задача 2.7.Напишите программу, вводящую два целых числа a и b, печатающую
их, затем обменивающую значения этих переменных (так, чтобы новое значение a стало равно старому значению b, и наоборот) и вновь их
печатающую, которая не использовала бы иных переменных, кроме $$a$$ и $$b$$.
Задача 2.8.Напишите программу, вводящую три целых числа, и печатающую второе по
величине, если оно существует, и No — иначе.
Задача 2.9.Напишите программу, вводящую действительное число, которая рассматривает это число, как координаты точки на прямой, и печатает расстояние от этой точки до отрезка $$[0,1]$$.
Задача 2.10.Напишите программу, вводящую три целых числа, и печатающую с использованием
всех возможностей класса Xterm как сами числа, так и их среднее
арифметическое.
Задача 2.11.Напишите программу, вводящую действительные коэффициенты $$a$$, $$b$$ и $$c$$ квадратного уравнения $$a x^2 + b x + c = 0$$ с положительным дискриминантом, находящую оба корня этого уравнения.
Эта секция содержит для справки программную реализацию класса Xterm,
который мы будем активно использовать всю первую половину нашего курса.
import java.io.*;
// Класс, обеспечивающий вывод строк текста с возможностью
// позиционирования и использования цветов, а также ввод чисел
// целых типов int и long и вещественных float и double.
public class Xterm {
private static final DataInputStream in =
new DataInputStream(System.in);
private static final int MAXLEN = 255;
private static String inputString() throws IOException {
byte buf[] = new byte[MAXLEN];
int i = in.read(buf);
return new String(buf,0,i-1);
}
// Имена цветов символов и фона
public static final int Black = 0;
public static final int Red = 1;
public static final int Green = 2;
public static final int Yellow = 3;
public static final int Blue = 4;
public static final int Magenta = 5;
public static final int Cyan = 6;
public static final int White = 7;
// Метод очистки экрана
public static void clear() {
System.out.print("\033[2J");
}
// Метод позиционирования курсора
public static void setPosition(int x, int y) {
System.out.print("\033[" + (y+1) + ";" + (x+1) + "H");
}
// Методы вывода строки
public static void print(String txt) {
System.out.print("\033[0m\033[30;1m"+txt+"\033[0m\033[30m");
}
public static void print(String txt, int fg) {
System.out.print("\033[0m\033[" + (30+fg)
+";1m" + txt + "\033[0m\033[30m");
}
public static void print(String txt, int fg, int bg) {
System.out.print("\033[0m\033["+(bg==7?"":""+(40+bg)+";")+
(30+fg)+";1m" + txt + "\033[0m\033[30m");
}
public static void println(String txt) {
print(txt + "\n");
}
public static void println(String txt, int fg) {
print(txt + "\n");
}
public static void println(String txt, int fg, int bg) {
print(txt + "\n");
}
// Методы ввода чисел типов int, long, float, double
public static int inputInt() throws IOException,
NumberFormatException {
return Integer.valueOf(inputString()).intValue();
}
public static int inputInt(String prompt) throws IOException,
NumberFormatException {
print(prompt); return inputInt();
}
public static long inputLong() throws IOException,
NumberFormatException {
return Long.valueOf(inputString()).longValue();
}
public static long inputLong(String prompt) throws IOException,
NumberFormatException {
print(prompt); return inputLong();
}
public static float inputFloat() throws IOException,
NumberFormatException {
return Float.valueOf(inputString()).floatValue();
}
public static float inputFloat(String prompt) throws IOException,
NumberFormatException {
print(prompt); return inputFloat();
}
public static double inputDouble() throws IOException,
NumberFormatException {
return Double.valueOf(inputString()).doubleValue();
}
public static double inputDouble(String prompt)
throws IOException, NumberFormatException {
print(prompt); return inputDouble();
}
// Методы ввода строки, рассматриваемой как массив символов.
public static char[] inputChars() throws IOException {
return (inputString()).toCharArray();
}
public static char[] inputChars(String prompt)
throws IOException {
print(prompt);
return (inputString()).toCharArray();
}
}
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.