-1Прикладное программирование в ТС Лекция 3-02 Лекция 03-02 2.1.6. Реализация объектно-ориентированного подхода в Java 2.1.7. Типы данных и объявления переменных 2.1.8. Примитивные типы Java 2.1.8.1. Целые типы переменных 2.1.8.2. Вещественные типы переменных 2.1.8.3. Булевский тип переменных 2.1.9. Операции над примитивными типами в Java 2.1.9.1. Операция присваивания 2.1.9.2. Унарные операции 2.1.9.3. Арифметические бинарные операции 2.1.9.4. Побитовые операции 2.1.9.5. Комбинированные операции 2.1.9.6. Операции сравнения 2.1.9.7. Булевские операции 2.1.9.8. Условная операция 2.1.9.9. Старшинство операций 2.1.9.10. Преобразование и приведение типов при выполнении операций 2.1.10. Методы в Java 2.1.11. Переменные типа классов в Java 2.1.11.1. Создание переменных типа классов 2.1.11.2. Переменные и методы объекта 2.1.11.3. Переменные и методы класса 2.1.11.4. Операции над объектами 2.1.11.5. Переменные перечислимого типа 2.1.12. Массивы в Java 2.1.12.1. Описание массивов 2.1.12.2. Доступ к элементам массива 2.1.12.3. Многомерные массивы 2.1.13. Операторы передачи управления в Java 2.1.13.1. Условный оператор 2.1.13.2. Операторы цикла 2.1.13.3. Операторы перехода 2.1.13.4. Оператор выбора 2.1.6. Реализация объектно-ориентированного подхода в Java Как и объекты реального мира, объекты программирования также имеют две характеристики: состояние и поведение, причем состояние объекта определяется его свойствами, а поведение – выполняемыми им действиями. Для объектно-ориентированных языков характерен следующий подход к программированию: все является объектом; программа является набором объектов, указывающих друг другу, что делать посредством посылки сообщений; каждый объект имеет некоторый тип; все объекты одного типа могут получать одни и те же сообщения. Объект – это набор данных и процедур, работающих с этими данными. Эти процедуры (подпрограммы) обработки данных объекта называются в Java методами. Таким образом, при объектно-ориентированном подходе любые объекты реального мира и абстрактные концепции можно легко воплотить в программы. Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. -2Прикладное программирование в ТС Лекция 3-02 На рис. 1.4 представлено взаимодействие программных объектов с окружающей средой: О Оббъъеекктт С Сооооббщ щееннииее М Мееттоодды ы О Оттввеетт Д Даанннны ыее Рис. 1.4. Взаимодействие программных объектов с окружающей средой Линия на объекте между методами и данными внутри объекта является условной, поскольку методы объекта имеют полный доступ к данным своего объекта. В тоже время эта линия показывает, что вне объекта данные, объявляемые в данном объекте, не видны, а все взаимодействие с объектом осуществляется только через методы. В общем, объект может быть разделен на две компоненты: внешнюю и внутреннюю. Внешнюю часть, которую часто называют интерфейсом, составляют методы, осуществляющие взаимодействие с остальной частью программы. Внутреннюю часть составляют данные и методы, доступные только внутри объекта. Такое сокрытие («затенение») данных и методов внутри объекта называется инкапсуляцией, т.е. инкапсуляция – это процесс упаковки данных объекта вместе с его методами. Результатом инкапсуляции является предотвращение нежелательного доступа извне к данным и методам внутри объекта и возможность изменения внутренней реализации объекта без изменения других частей программы. Объекты программы взаимодействуют и связываются друг с другом с помощью сообщений. Информация, необходимая объекту для выполнения тех или иных методов данного объекта передается в сообщении с помощью параметров. Таким образом, сообщение должно содержать три компонента: объекта – получателя сообщения; имени выполняемого действия; параметров, необходимых для выполнения действия. В Java посылка сообщения означает вызов метода с именем выполняемого действия, а параметры для выполнения метода задаются с помощью аргументов метода, т.е. сообщения и методы в Java являются синонимами. Следует также отметить, что интерфейсы позволяют объектам передавать и принимать сообщения, даже если они расположены в разных узлах сети, поскольку язык Java был специально разработан с учетом его функционирования в компьютерной сети. Более подробно интерфейсы будут рассмотрены далее. При рассмотрении объектов предполагалось, что они уже существуют в системе. Чтобы определить объект, в Java и других объектно-ориентированных языках используется фундаментальное понятие: класс. Описывая поведение какого-либо объекта, например автомобиля, мы строим его модель. Модель, как правило, не может описать объект полностью, поскольку реальные Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. -3Прикладное программирование в ТС Лекция 3-02 объекты слишком сложны. Приходится отбирать только те характеристики объекта, которые важны для решения поставленной перед нами задачи. Необходимо абстрагироваться от некоторых конкретных деталей объекта. Очень важно выбрать правильную степень абстракции, поскольку слишком высокая степень даст только приблизительное описание объекта, не позволит правильно моделировать его поведение, а слишком низкая степень абстракции сделает модель очень сложной, перегруженной деталями, и потому непригодной. Описание каждой модели производится в виде одного или нескольких классов (classes). Класс – это шаблон или прототип, определяющий тип объекта. Класс содержит описание переменных и констант, характеризующих свойства объекта. Они называются полями класса (class fields). Процедуры, описывающие поведение объекта, называются, как уже говорилось, методами класса (class methods). Когда объект создается из класса, переменные, объявленные для этого класса, размещаются в памяти. Затем переменные могут модифицироваться с помощью методов объекта. Реализации одного класса совместно используют реализации методов класса, но каждая из них использует свои собственные данные объекта, т.е. класс можно повторно использовать для реализации нескольких объектов с одинаковыми методами, но разными значениями данных. Объявление класса в Java имеет следующий формат: class идентификатор-класса { тело-класса } где идентификатор определяет имя класса. В соглашениях по кодированию рекомендуется начинать идентификатор класса с большой буквы. Если имя класса состоит из нескольких слов, то все они пишутся слитно, причем каждое слово начинается с большой буквы, например: MyFirstClass. В теле класса (и только в теле класса) определяются его переменные и методы. Таким образом, программа на языке Java представляет собой объявления одного или нескольких классов. Каждый класс в программе компилируется в отдельный файл с именем идентификатор-класса.class. В издания Java 2 входят сотни классов, которые можно использовать при создании программ. Кроме того, при разработке программ создаются и свои классы, число которых может быть довольно большим. Поэтому разработчики Java включили в язык дополнительную конструкцию – пакеты (packages). Все классы Java распределены по пакетам (обычно по функциональному признаку, например, классы-утилиты или классы ввода-вывода). Кроме классов, пакеты могут включать в себя разновидность классов – интерфейсы (будут рассмотрены позднее) и вложенные подпакеты (subpackages). В результате образуется древовидная структура пакетов и подпакетов. Эта структура в точности соответствует древовидной структуре файловой системы. Все файлы с расширением .class (содержащие байт-коды), образующие пакет, хранятся в одном каталоге, а подпакеты хранятся в подкаталогах этого каталога. Пакет однозначно идентифицируется своим именем, перед которым, отделенные друг от друга точкой, идут имена всех пакетов, находящихся выше данного пакета в уровнях иерархии. Например, имя пакета: java.awt.event обозначает имя подпакета event, находящегося в подпакете awt, который, в свою очередь, находится в пакете java, а имя: javax.swing.event Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. -4Прикладное программирование в ТС Лекция 3-02 обозначает имя другого подпакета с тем же именем event, но который находится в подпакете swing пакета javax. Каждый пакет образует одно пространство имен (namespace). Это означает, что все имена классов, интерфейсов и подпакетов в пакете должны быть уникальны. Имена в разных пакетах могут совпадать, но это будут разные программные единицы. Таким образом, ни один класс, интерфейс или подпакет не может оказаться сразу в двух пакетах. Если надо использовать два класса с одинаковыми именами из разных пакетов, то имя класса уточняется именем пакета: пакет.класс. Такое уточненное имя называется полным именем класса (fully qualified class name). Например, имя java.util.Date указывает на класс Date в пакете java.util, а имя java.sql.Date указывает на другой класс с тем же именем Date, но в пакете java.sql. Первая версия JDK (JDK 1.0) включала в себя 9 пакетов программ. В последующих версиях добавлялись новые классы и интерфейсы как в существующих пакетах, так и в созданных новых пакетах, и в состав Java SE 6 включено около двухсот пакетов, охватывающих самые разные предметные области программирования. Ниже представлены некоторые из основных пакетов Java: java.lang – классы, образующие ядро языка Java, в том числе классы определения базовых типов объектов, математических методов, организации потоков и обработки ошибок; java.awt – средства оконного пользовательского интерфейса (Abstract Window Toolkit, AWT), позволяющие создавать графические интерфейсы для программ; java.applet – содержит определение класса Applet, а также методы и интерфейсы, используемые для программирования апплетов; java.util – содержит классы и методы, которые могут оказаться полезными при разработке программ (например, средства работы с датами, организации массивов переменной длины и т.д.). При компиляции необходимые для выполнения программы классы пакетов Java, за исключением пакета java.lang, автоматически не включаются. Чтобы сделать их доступными в программе на языке Java, можно либо указывать полное имя класса, либо использовать оператор или операторы import с именем пакета и именем используемого класса данного пакета, например: import java.util.Date; делает доступным программе класс Date из пакета java.util. (в этом случае класс доступен в программе просто по своему имени Date). Если необходимо использовать несколько классов или интерфейсов из пакета, обычно вместо имени класса или интерфейса ставится символ "*", что указывает, что данной программе будут доступны все классы и интерфейсы данного пакета, например, оператор: import java.awt.*; делает доступными программе все классы из пакета java.awt. Операторы import должны быть заданы в программе до первого использования заданного в нем класса или классов, однако принято располагать все операторы import в самом начале программы (до объявления первого класса). Операторы внутри класса всегда может обращаться ко всем полям и методам данного класса. Однако по умолчанию методы и переменные класса доступны только в том пакете, где класс определен. Чтобы сделать методы и переменные класса доступными во всех пакетах, необходимо перед ключевым словом класс указать модификатор доступа public, например: Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. -5Прикладное программирование в ТС Лекция 3-02 public class publicClass { // Тело класса } Рассмотрим теперь, как определяются в Java переменные и методы. 2.1.7. Типы данных и объявления переменных Языки интерпретирующего типа, не связывают жестко переменную с тем набором значений, которая она может принимать. Это означает, что в ходе выполнения программа одной и той же переменной может быть сначала присвоено числовое значения, т.е. переменная будет иметь числовой тип, затем ей может быть присвоено символьное значение, т.е. переменная станет символьного типа. Такие языки (например, используемый при программировании Web-страниц язык JavaScript) называют языками со свободной типизацией (loose typed language). Язык программирования Java, как и другие языки компилирующего типа (например, C/C++), является языком со строгой типизацией (strongly typed language). Это означает, что каждая переменная и каждое выражение имеют тип, который должен быть известен во время компиляции программы. Тип ограничивает набор значений, которые могут быть присвоены переменной, либо получены в выражении, ограничивает операции над значениями и определяет реализацию конкретной операции. В Java определено две категории данных: примитивные типы (primitive types); ссылочные типы (reference types). Существует также специальный нулевой тип, тип выражения null, который не имеет имени. Примитивные типы делятся на булевский тип (boolean) и числовые типы. В свою очередь, числовые типы – это целые типы (byte, short, int, long и char) вещественные типы (float и double). Ссылочные типы – это типы классов, интерфейсов и массивов. Иерархическая схема типов Java представлена на рис. 1.5. Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. -6Прикладное программирование в ТС Лекция 3-02 ТТииппы ы ддаанннны ыхх JJaavvaa С Сссы ыллооччнны ыее ттииппы ы П Прриим мииттииввнны ыее ттииппы ы bboooolleeaann Ч Чииссллооввы ыее ттииппы ы М Маассссииввы ы К Кллаассссы ы Ц Цееллы ыее ттииппы ы bbyyttee sshhoorrtt iinntt И Иннттееррф фееййссы ы ВВеещ щеессттввеенннны ыее ттииппы ы lloonngg cchhaarr ffllooaatt ddoouubbllee Рис. 1.5. Типы переменных языка Java Объявление переменных (за исключением массивов) в Java имеет следующий вид: имя-типа идентификатор-переменной; например: int x; String string1; Если несколько переменных имеют один и тот же тип, их можно объявить в одном предложении, перечислив идентификаторы переменных через запятую: имя-типа идентификатор-переменной-1, идентификатор-переменной-2,…; например: int x1, x2, x3; В соглашениях по кодированию рекомендуется начинать идентификаторпеременной с маленькой буквы. Если имя переменной состоит из нескольких слов, то все они пишутся слитно, причем каждое слово, кроме первого, начинается с большой буквы, например: double inputValue; Переменная является указанием места хранения значения переменной в памяти. Переменная примитивного типа всегда содержит значение переменной указанного типа (например, int), а переменная ссылочного типа хранит ссылку (адрес) объекта указанного типа. Создавать новые переменные можно в любом месте программы. Любое объявление переменной имеет свою область действия, границы которой зависят от того, где именно расположено это объявление. При помещении фрагмента Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. -7Прикладное программирование в ТС Лекция 3-02 текста программы в пару фигурных скобок { } создается новый блок. Блоки в программе на языке Java могут быть вложенными. Переменная доступна в блоке только в том случае, если она определена в этом блоке или в одном из вышестоящих блоков, в который входит текущий блок, например: // Блок 1 { int x; … // Блок 2 { int y; … // Конец блока 2 } // Конец блока 1 } Переменная x определена и в блоке 1 и в блоке 2, а переменная y – только в блоке 2. В отличие от языка C, во внутреннем блоке нельзя объявлять переменную с таким же именем, как во внешней области действия. Поэтому следующий фрагмент программы int x = 1; { int x = 2; } приведет к ошибке при компиляции программы (в C каждая переменная x была бы действительна только в своем блоке). Помимо области действия, каждая переменная имеет и время жизни. Когда текущий блок, в котором переменная была объявлена, заканчивается, она становится доступной для уничтожения с помощью так называемого сборщика мусора. Изменить область действия и время жизни переменной можно с помощью модификаторов доступа. Модификаторы вставляются в объявление переменной перед именем-типа. По умолчанию (без модификатора) переменная доступна только классам в том же пакете, что и класс, в котором она содержится. Модификатор public определяет, что переменная доступна как внутри, так и вне класса, т. е. переменная является глобальной и доступна любому другому объекту. Так, если переменная publicVariable в классе A1Class пакета packet1 объявлена с модификатором public: public int publicVariable; то она доступна и в классе B1Class пакета packet2. Модификатор private, наоборот, означает, что переменная доступна только в том классе, в котором она была объявлена. Модификатор final определяет, что переменная имеет постоянное (неизменное) значение и не может быть переопределена. Так, при попытке изменить значение переменной PI, объявленной как public final PI = 3.14; будет выдано сообщение об ошибке, и выполнение программы будет прекращено. Следует отметить, что согласно соглашению о кодировании, имена констант записываются полностью прописными буквами; если имя состоит из нескольких слов, то между ними ставится знак подчеркивания, например: int final GREEN_COLOR =2; Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. -8Прикладное программирование в ТС Лекция 3-02 2.1.8. Примитивные типы Java Как уже говорилось, в Java определены следующие примитивные типы: целые типы; вещественные типы; булевский тип. За оператором объявления примитивной переменной может следовать оператор инициализации "=", с помощью которого созданной переменной присваивается начальное значение. 2.1.8.1. Целые типы переменных Целые типы различаются по размеру отведенной для них памяти. Характеристики целочисленных типов приведены в табл. 1.1. Таблица 1.1. Характеристики целочисленных типов Java Тип Размер (бит) Минимальное значение Максимальное значение byte 8 -128 127 short 16 -32768 32767 int 32 -2147483648 2147483647 long 64 -922372036854775808 922372036854775807 char 16 0 65536 Как видно из приведенной таблицы, целые переменные, за исключением типа char, считаются в языке Java переменными со знаком, т.е. первый бит в двоичном представлении числа всегда является знаковым (0 для положительных чисел и 1 – для отрицательных). Переменные типа char являются переменными без знака, т.е. все биты в двоичном виде представляют число (таким образом могут быть заданы только положительные числа). Целочисленные константы могут задаваться в программе одним из трех способов: в виде десятичных, шестнадцатеричных и восьмеричных значений. По умолчанию все числа интерпретируются как десятичные и относятся к типу int. Явно указать принадлежность к типу long можно, добавив в конце числа букву "l" или букву "L". Шестнадцатеричное значение задается с помощью символов "0x" или "0X", за которым значение числа (цифры 0-9 и буквы A-F или a-f), например: 0x7FFF. Число в восьмеричной записи должно начинаться с нуля, за которым следует одна или несколько восьмеричных цифр, например 077777. Восьмеричные и шестнадцатеричные числа могут быть как положительными, так и отрицательными и изменяются в тех же диапазонах, что и числа в десятичном представлении (например, шестнадцатеричные числа типа byte имеют максимальное значение 0x7F и минимальное значение -0x80, а восьмеричные – соответственно 177 и -200). Примеры объявления целых переменных: int x = 0; long i, j, k; byte a1 = 0xF1, a2 = 0x07; short r1 = 017; Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. -9Прикладное программирование в ТС Лекция 3-02 Символы в Java определяются с помощью ключевого слова char и реализованы с использованием стандарта Unicode. Можно задать константу-символ в программе как обычный символ. Символьное значение должны быть заключено в пару одиночных апострофов, например char symbol='f'; Другой способ записи символов: пара символов "\u", за которой следует четырехзначное шестнадцатеричное число (в диапазоне от 0000 до FFFF), представляющее собой код символа в Unicode, например char symbol = '\u0042'; Некоторые символы, отсутствующие на клавиатуре, можно задавать с помощью так называемых escape-последовательностей, содержащих символ "\", за которым следует буквенный символ, идентифицирующий escape-последовательность, как показано в табл. 1.2. Таблица 1.2. Escape-последовательности, используемые в языке Java EscapeЗначение в Функция последовательность Unicode \b \u0008 Забой (backspace) \t Горизонтальная табуляция (horizontal tab) \u0009 \n Перевод строки (linefeed) \u000A \f Перевод страницы (form feed) \u000C \r Возврат каретки (carriage return) \u000D \" Двойная кавычка (double quote) \u0022 \' Апостроф (single quote) \u0027 \\ Обратная косая черта (backslash) \u005C 2.1.8.2. Вещественные типы переменных Язык Java поддерживает числа и переменные с плавающей точкой обычной и двойной разрядности – типы float и double. Для чисел с плавающей точкой нужно указывает целую и дробную часть, разделенные точкой, например 4.6 или 7.0. Для больших чисел можно использовать экспоненциальную форму записи (для отделения мантиссы от порядка используется символ "e" или символ "E"), например, число -3,58107 записывается как –3.58E7, а число 73,67510-15 – как 73.675e-15. Характеристики вещественных типов Java представлены в табл. 1.3. Переменные с плавающей точкой могут хранить не только численные значения, но и любой из особо определенных флагов (состоянии): отрицательная бесконечность, отрицательный нуль, положительная бесконечность, положительный нуль и «отсутствие числа» (not-a-number, NaN). Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 10 Прикладное программирование в ТС Лекция 3-02 Таблица 1.3. Характеристики вещественных типов Java Тип Разрядность Диапазон (бит) Точность float 32 3,4e-38 < |x| < 3,4e38 7-8 цифр double 64 1,7e-308 < |x| < 1,7e308 17 цифр Все константы с плавающей точкой подразумеваются принадлежащими к типу double. Чтобы задать число типа float, необходимо добавить в его конец символ "f" или символ "F". Примеры объявления переменных с плавающей точкой: float x1 = 3.5f, x2 = 3.7E6f, x3 = -1.8E-7f; double z = 1.0; 2.1.8.3. Булевский тип переменных Переменные булевского типа (логические переменные) могут принимать одно из двух значений: «истина» или «ложь» и используются в языках программирования в операциях отношения (сравнения) и логических операциях. Так, результатом сравнения 5 > 3 будет «истина», а результатом сравнения 8 < 1 будет «ложь». В Java для булевских переменных введен свой, отдельный тип данных – булевский. Переменные булевского типа в Java задаются с помощью ключевого слова boolean и могут иметь лишь одно из двух значений: true или false, например boolean switch = true; 2.1.9. Операции над примитивными типами в Java Большинство операций над примитивными типами выполняется не с помощью методов, а с помощью специальных символов, называемых знаком операции. 2.1.9.1. Операция присваивания Присвоение переменной значения константы, другой переменной или выражения (переменных и/или констант, разделенных знаками операций), называется операцией присваивания и обозначается знаком "=", например: x = 3; y = x; z = x; В Java допустимо многократное использование операции присваивания в одном выражении, например: x1 = x2 = x3 = 0; Эта операция выполняется справа налево, т.е. сначала переменной x3 присваивается значение 0, затем переменной x2 присваивается значение переменной x3 (0), и, наконец, переменной x1 присваивается значение переменной x2 (0). Знаки операций, аргументами которых являются числа, разделяются на две категории: унарные (unary) знаки операций с одним аргументом и бинарные (binary) с двумя аргументами. 2.1.9.2. Унарные операции В Java определены следующие унарные операции: Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 11 Прикладное программирование в ТС Лекция 3-02 унарный минус "-" – меняет знак числа или выражения на противоположный; унарный плюс "+" – не выполняет никаких действий над числом или выражением; побитовое дополнение "~" (только для целых) – инвертирует все биты поля числа (меняет 0 на 1 и 1 на 0); инкремент "++" – увеличивает значение переменной на 1; декремент "--" – уменьшает значение переменной на 1. Примеры унарных операций "+" и "-": int i = 3, j, k; j= -i; // j = -3 k = +i; // k = 3 Пример операции побитового дополнения: int a = 15; int b; b = ~a; // b = -16 Числа a и b являются числами типа int, т.е. представляются внутри компьютера как двоичные целые числа со знаком длиной 32 бита, поэтому двоичное представление чисел a и b будет выглядеть следующим образом: a = 00000000 00000000 00000000 00001111 b = 11111111 11111111 11111111 11110000 Как видно из этого представления, все нулевые биты в числе a изменены на единичные биты в числе b, а единичные биты в a изменены на нулевые биты. Десятичным представлением числа b будет –16. Знаки операции инкремента и декремента могут размещаться как до, так и после переменной. Эти варианты называются соответственно префиксной и постфиксной записью этих операции. Знак операции в префиксной записи возвращает значение своего операнда после вычисления выражения. При постфиксной записи знак операции сначала возвращает значение своего операнда и только после этого вычисляет инкремент или декремент, например: int x = 1, y, z; y = ++x; z=x++; Переменная y будет присвоено значение 2, поскольку сначала значение x будет увеличено на 1, а затем результат будет присвоен переменной y. Переменной z будет присвоено значение 1, поскольку сначала переменной z будет присвоено значение, а затем значение x будет увеличено на 1. В обоих случаях новое значение переменной x будет равно 2. Следует отметить, что в Java, в отличие от языка C, операции декремента и инкремента могут применяться и к вещественным переменным (типа float и double). Бинарные операции подразделяются на: арифметические операции; побитовые операции; комбинированные операции; операции сравнения. 2.1.9.3. Арифметические бинарные операции В Java определены следующие арифметические бинарные операции: Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 12 Прикладное программирование в ТС Лекция 3-02 сложение "+"; вычитание "-"; умножение "*"; деление "/"; вычисление остатка от деления целых чисел "%" . Последняя операция возвращает остаток от деления первого числа на второе, причем результат будет иметь тот же знак, что и делимое), например, результат операции 5%3 будет равен 2, а результат операции (-7)%(-4) будет равен -3. В Java операция может использоваться и для вещественных переменных (типа float или double). Примеры бинарных арифметических операций: int x = 7, x1, x2, x3, x4, x5; x1 = x + 15; // x1 = 17 x2 = x – 8; // x2 = -1 x3 = x2 * x; // x3 = -7 x4 = x/4; // x4 = 1 (при делении целых чисел // дробная часть отбрасывается) x5 = x%4 // x5 = 3 (остаток от деления // 7 на 4) 2.1.9.4. Побитовые операции Побитовые операции рассматривают исходные числовые значения как поля битов и выполняют над ними следующие действия: установка бита в i-ой позиции поля результата в 1, если оба бита в i-ых позициях операндов равны 1, или в 0 в противном случае – побитовое И ("&"); установка бита в i-ой позиции поля результата в 1, если хотя бы один бит в i-ых позициях операндов равен 1, или в 0 в противном случае – побитовое ИЛИ ("|"); установка бита в i-ой позиции поля результата в 1, если биты в i-ых позициях операндов не равны друг другу, или в 0 в противном случае – побитовое исключающее ИЛИ ("^"); сдвиг влево битов поля первого операнда на количество битов, определяемое вторым операндом (бит знака числа при этом не меняется) – побитовый сдвиг влево с учетом знака "<<"; сдвиг вправо битов поля первого операнда на количество битов, определяемое вторым операндом (бит знака числа при этом не меняется) – побитовый сдвиг вправо с учетом знака ">>"; сдвиг вправо битов поля первого операнда на количество битов, определяемое вторым операндом (бит знака числа при этом также сдвигается) – побитовый сдвиг вправо без учета знака ">>>". Примеры побитовых операций: 1. Побитовое И int x = 112; // x: 00000000 00000000 00000000 01110000 int y = 94; // y: 00000000 00000000 00000000 01011110 Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 13 Прикладное программирование в ТС Лекция 3-02 int z; z = x & y; // z=80: 2. Побитовое ИЛИ int x = 112; // x: int y = 94; // y: int z; z =x | y; // z = 126: 00000000 00000000 00000000 01010000 00000000 00000000 00000000 01110000 00000000 00000000 00000000 01011110 00000000 00000000 00000000 01111110 3. Побитовое исключающее ИЛИ int x = 112; // x: 00000000 00000000 00000000 01110000 int y = 94; // y: 00000000 00000000 00000000 01011110 int z; z =x ^ y; // z = 46: 00000000 00000000 00000000 00101110 4. Сдвиг влево с учетом знака int x = 31, z; // x: 00000000 00000000 00000000 00011111 z = x << 2; // z = 124: 00000000 00000000 00000000 01111100 5. Сдвиг вправо с учетом знака int x = -17, z; // x: 11111111 11111111 11111111 11101111 z = x >> 2; // z = -5: 11111111 11111111 11111111 11111011 6. Сдвиг вправо без учета знака int x = -17, z; // x: 11111111 11111111 11111111 11101111 z = x >>> 2; // z = 1073741819 // z: 00111111 11111111 11111111 11111011 2.1.9.5. Комбинированные операции В Java для бинарных арифметических операций комбинированные (составные) знаки операций: идентификатор операция = выражение Это эквивалентно следующей операции: идентификатор = идентификатор операция выражение можно использовать Примеры: 1. Выражение x += b означает x = x + b. 2. Выражение x -= b означает x = x - b. Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 14 Прикладное программирование в ТС Лекция 3-02 3. Выражение x 4. Выражение x 5. Выражение x 6. Выражение x 7. Выражение x 8. Выражение x 9. Выражение x 10. Выражение x 11. Выражение x *= b означает x = x * b. /= b означает x = x / b. %= b означает x = x % b. &= b означает x = x & b. |= b означает x = x | b. ^= b означает x = x ^ b. <<= b означает x = x << b. >>= b означает x = x >> b. >>>= b означает x = x >>> b. 2.1.9.6. Операции сравнения В Java определены следующие операции сравнения: "==" (равно), "!=" (не равно), ">" (больше), ">=" (больше или равно), "<" (меньше) "<=" (меньше или равно) Эти операции имеют два операнда и возвращают булевское значение, соответствующее результату сравнения (false или true). Следует обратить внимание, что при сравнении двух величин на равенство в Java, как и в C и в C++, используются символы "==" (два идущих без пробела друг за другом знака равенства), в отличие от оператора присваивания, в котором используется символ "=". Использование символа "=" при сравнении двух величин либо вызывает ошибку при компиляции, либо приводит к неверному результату. Примеры операций сравнения: boolean isEqual, isNonEqual, isGreater, isGreaterOrEqual, isLess, isLessOrEqual; int x1 = 5, x2 = 5, x3 = 3, x4 = 7; isEqual = x1 == x2; // isEqual = true isNonEqual = x1 != x2; // isNonEqual = false isGreater = x1 > x3; // isGreater = true // isGreaterOrEqual = true isGreaterOrEqual = x2 >= x3; isLess = x3 < x1; // isLess = true isLessOrEqual = x1 <= x3; // isLessOrEqual = false 2.1.9.7. Булевские операции Булевские операции выполняются над булевскими переменными и их результатом также является значение типа boolean. В Java определены следующие булевские операции: отрицание "!" – замена false на true, или наоборот; операция И "&" – результат равен true, только, если оба операнда равны true, иначе результат – false; операция ИЛИ "|" – результат равен true, только, если хотя бы один из операндов равен true, иначе результат – false. операция исключающее ИЛИ "^" – результат равен true, только, если операнды не равны друг другу, иначе результат – false. Операции "&", "|" и "^" можно, также как и соответствующие побитовые операции, использовать в составных операциях присваивания: "&=", "|=" и "^=". Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 15 Прикладное программирование в ТС Лекция 3-02 Кроме того, к булевским операндам применимы операции "==" (равно) и "!=" (не равно). Как видно из определения операций ИЛИ и И, операция ИЛИ приводит к результату true, когда первый операнд равен true, независимо от значения второго операнда, а операция И приводит к результату false, когда первый операнд равен false, независимо от значения второго операнда. В Java определены еще две булевские операции: вторые версии булевских операций И и ИЛИ, известные как укороченные (short-circuit) логические операции: укороченное И "&&" и укороченное ИЛИ "||". При использовании этих операций второй операнд вообще не будет вычисляться, что полезно в тех случаях, когда правильное функционирование правого операнда зависит от того, имеет ли левый операнд значение true или false. Примеры булевских операций: boolean isInRange, isValid, isNotValid, isEqual, isNotEqual; int x = 8; isInRange = x > 0 && x < 5; // isInRange = false isValid = x > 0 || x > 5; // isValid = true isNotValid = !isValid; // isNotValid = false isEqual = isInRange == isValid; // isEqual = false // isNotEqual = true isNotEqual = isInRange != isValid 2.1.9.8. Условная операция Условная операция записывается в форме выражение-1?выражение-2:выражение-3. При этом сначала вычисляется выражение выражение-1, которое должно дать булевское значение, а затем, если выражение-1 имеет значение true, вычисляется и возвращается выражение-2 как результат выполнения операции, либо (если выражение-1 имеет значение false), вычисляется и, как результат выполнения операции, возвращается выражение-3. Пример условной операции: x=n>1?0:1; Переменной x будет присвоено значение 0, если n>1 (выражение n>1 имеет значение true) или 1, если n≤1 (выражение n>1 имеет значение false). 2.1.9.9. Старшинство операций Операции в выражениях выполняются слева направо, однако, в соответствии со своим приоритетом. Так операции умножения в выражении y = x +z*5; будет выполнена раньше, чем операция сложения, поскольку приоритет операции умножения выше, чем приоритет операции сложения. Приоритеты операций (в порядке уменьшения приоритета) в Java приведены в табл. 1.4. Круглые скобки повышают старшинство операций, которые находятся внутри них. Так, если в приведенное выше выражение вставить скобки: y = (x +z)*5; Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 16 Прикладное программирование в ТС Лекция 3-02 то сначала будет выполнена операция сложения, а затем операция умножения. Иногда скобки используют просто для того, чтобы сделать выражение более читаемым, например: (x > 1) && (x <= 5). Таблица 1.4. Приоритеты операций в Java Операция Описание () [ ] Круглые и квадратные скобки ++ -- + - ~ ! Декремент, инкремент, унарный плюс, унарный минус, поразрядное отрицание, логическое отрицание * / % Умножение, деление, деление нацело + Сложение, вычитание >> >>> << Побитовый сдвиг вправо, побитовый сдвиг вправо с заполнением старшего бита нулем, побитовый сдвиг влево > >= < <= Сравнение на больше, больше или равно, меньше, меньше или равно == != Сравнение на равенство, сравнение на неравенство & Поразрядное и логическое И ^ Поразрядное и логическое исключающее ИЛИ | Поразрядное и логическое ИЛИ && Логическое укороченное И || Логическое укороченное ИЛИ ? : Условная операция =op Комбинированные операции (op – одна из арифметических или побитовых операций) 2.1.9.10. Преобразование и приведение типов при выполнении операций В операции присваивания и арифметических выражениях могут использоваться литералы, переменные и выражения разных типов, например: double y; byte x; y = x + 5; В этом примере выполняется операция сложения переменной x типа byte и литерала 5 (типа int) и результат присваивается переменной y типа double. В Java преобразования типов при вычислении выражений могут выполняться автоматически, либо с помощью оператора приведения типа, однако правила приведения являются более строгими, чем в других языках программирования (например, C/C++). При выполнении операции присваивания преобразование типов происходит автоматически, если происходит расширяющее преобразование (widening conversion) и два типа совместимы. Расширяющими преобразованиями являются следующие преобразования: byteshortintlongfloatdouble. Для расширяющих преобразований числовые типы, включая целый и с плавающей точкой, являются совместимыми друг с другом. Однако числовые типы не совместимы с типами char и boolean. Типы char и boolean не совместимы также и друг с другом. В языке Java выполняется автоматическое преобразование типов также и при сохранении литеральной целочисленной константы (которая имеет по умолчанию тип int) в переменных типа byte, short или long (однако если литерал имеет значение Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 17 Прикладное программирование в ТС Лекция 3-02 вне диапазона допустимых значений для данного типа, выдается сообщение об ошибке: возможная потеря точности). Если преобразование является сужающим (narrowing conversion), т. е. выполняется преобразование byteshortcharintlongfloatdouble, то такое преобразование может привести к потере точности числа или к его искажению. Поэтому при сужающих преобразованиях при компиляции программы выводится диагностическое сообщение о несовместимости типов и файлы классов не создаются. Такое сообщение будет выдано и при попытке преобразование выражений типа byte или short в переменную типа char. Если все же необходимо выполнить такие преобразования, используется операция приведения (cast) типа, которая имеет следующий формат: (тип-преобразования) значение где тип-преобразования определяет тип, в который необходимо преобразовать заданное значение, например, в результате выполнения операторов: byte x = 71; char symbol = (char) x; переменная symbol получит значение 'G'. Если значение с плавающей точкой присваивается целому типу, то (если значение с плавающей точкой имеет дробную часть) при явном преобразовании типа происходит также усечение (truncation) числа. Так, в результате выполнения оператора int x = (int) 77.85; переменная x получит значение 77. Если же присваиваемое значение лежит вне диапазона типа-преобразования, то результатом преобразования будет остаток от деления значения на модуль диапазона присваиваемого типа (для чисел типа byte модуль диапазона будет равен 256, для short – 65536, для int – 4294967296 и для long – 18446744073709551616). Например, в результате выполнения оператора byte x = (byte) 514; переменная x получит значение 2. При преобразовании целых или вещественных чисел в данные типа char, преобразование в символ происходит, если исходное число лежит в диапазоне от 0 до 127, иначе символ получает значение '?'. При выполнении арифметических и побитовых преобразований все значения byte и short, а также char расширяются до int, (при этом в вычислениях для char используется числовое значение кода символа) затем, если хотя бы один операнд имеет тип long, тип целого выражения расширяется до long. Если один из операндов имеет тип float, то тип полного выражения расширяется до float, а если один из операндов имеет тип double, то тип результата будет double. Так, если объявлены переменные byte a, c; short b; то в выражении a + b*c – 15L + 1.5F + 1.08 - 10; сначала, перед вычислением a + b*c значения переменных будут расширены до int, затем, поскольку константа 15 имеет тип long, перед вычитанием результат вычисления будет увеличен до long. После этого, поскольку литерал 1.5 имеет тип float перед сложением с этим литералом результат вычисления a + b*c – 15L будет расширен до float. Перед выполнением сложения с числом 1.08 результат предыдущих вычислений будет расширен до double (поскольку вещественные константы по умолчанию имеют тип double) и, наконец, перед выполнением последнего сложения литерал 10 (по Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 18 Прикладное программирование в ТС Лекция 3-02 умолчанию int) будет расширен до double. Таким образом, результат вычисления выражения будет иметь тип double. Автоматические расширения типов (особенно расширения short и byte до int) могут вызывать плохо распознаваемые ошибки во время компиляции. Например, в операторах: byte x = 30, y =5; x = x + y; перед выполнением умножения значение переменных x и y будет расширено до int, а затем при выполнении попытки присвоения результата вычисления типа int переменной типа byte будет выдано сообщение об ошибке. Чтобы этого избежать надо использовать во втором операторе явное преобразование типов: x = (byte) (x + y); Выражение x + y необходимо заключить в скобки потому, что приоритет операции приведения типа, заключенной в скобки, выше, чем приоритет операции сложения. Однако, если записать второй оператор в виде: x += y; то сообщения об ошибке не будет. 2.1.10. Методы в Java Для определения методов используется следующий формат: возвращаемый-тип идентификатор-метода (параметры) { тело-метода } Возвращаемый-тип определяет тип данных, которые возвращает метод при вызове (возвращаемый ответ на сообщение). Если метод не возвращает никакого значения, то возвращаемый-тип имеет значение void. Идентификатор-метода определяет имя метода, а параметры – список параметров, которые необходимо передать методу при его вызове. Параметры в списке отделяются друг от друга запятыми, и каждый параметр имеет следующий вид: тип идентификатор где тип определяет тип параметра, а идентификатор – имя параметра (область действия этого имени – только внутри тела метода). Если параметры отсутствуют, после идентификатора-метода указываются пустые скобки. Идентификатор метода формируется, по тем же правилам, что и имя переменной, т.е. первая буква имени должна быть строчной, а остальные слова, составляющие имя, начинаются с большой буквы, например method1 или myMethod. Тело-метода содержит операторы, реализующие действия, выполняемые данным методом. Если тип возвращаемого значения не void, в теле метода должен быть хотя бы один оператор return выражение; где тип выражения должен совпадать с типом возвращаемого значения. Этот оператор возвращает результат вычисления выражения в точку вызова метода. Если тип возвращаемого значения – void, возврат из метода выполняется либо после выполнения последнего оператора тела метода, либо в результате выполнения оператора Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 19 Прикладное программирование в ТС Лекция 3-02 return; (таких операторов в теле метода может быть несколько). Пример объявления метода, возвращающего значение типа int – сумму двух своих параметров типа int: int sumOfTwoValues(int a, int b) { int x; x = a + b; return x; } (тело цикла можно также записать с помощью одного оператора: return a + b). При вызове метода, например, sumOfTwoValues(5, 3), параметры 5 и 3 передаются в метод, как значения соответственно a и b, и оператор вызова метода sumOfTwoValues(5, 3)– заменяется значением, возвращаемым методом (8). Тип задаваемого параметра в Java должен строго соответствовать типу параметра в объявлении метода, поэтому вызов метода sumOfTwoValues(1.5, 8) приведет к ошибке при компиляции программы. В языке Java в пределах одного класса можно определить два или более методов, которые совместно используют одно и то же имя, но имеют разное количество параметров. Когда это имеет место, методы называют перегруженными, а о процессе говорят как о перегрузке метода (method overloading). Когда метод вызывается, то по количеству параметров и/или их типам среда выполнения Java определяет, какую именно версию перегруженного метода надо вызывать (тип возвращаемого значения во внимание не принимается, хотя, в принципе, он тоже может отличаться у разных версий перегруженных методов). Например, метод double sumOfTwoValues(double a, double b) { double x; x = a + b; return x; } вместе с методом int sumOfTwoValues(int a, int b)составляют пару перегруженных методов и при вызове sumOfTwoValues(5, 8) будет вызван первый метод, а при вызове sumOfTwoValues(5.0, 8.0) будет вызван второй метод. По умолчанию метод, как и переменная, доступен только классам в том же пакете (наборе классов), что и исходный класс. Если перед возвращаемым типом задан модификатор доступа public, то метод является глобальным и доступен любым объектам, а модификатор private означает, что метод доступен в том классе, в котором он был объявлен, т.е. метод инкапсулирован в данном классе. В программе на языке Java можно использовать методы, реализация которых, выполнена во внешнем файле, в программе написанном на языке C/C++. Для этого перед определением метода (но без тела метода) указывается модификатор native, например: native int calcTotal(); 2.1.11. Переменные типа классов в Java 2.1.11.1. Создание переменных типа классов Как уже говорилось, объявление переменной (кроме массива) в Java имеет следующий вид: Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 20 Прикладное программирование в ТС Лекция 3-02 имя-типа идентификатор-переменной; где имя-типа для переменных типа классов является идентификатором класса, например: MyClass obj1; MyClass obj2; объявляет две переменных класса MyClass с именами obj1 и obj2. Чтобы переменную можно было использовать, она должна иметь определенное значение. Переменные примитивных типов получают значения с использованием операции присваивания либо при объявлении переменной, либо перед первым использованием переменной. Аналогично переменным примитивных типов, переменные ссылочных типов также перед своим использованием должны получить определенные значения. Для объявленной же переменной типа класса необходимо создать конкретный объект, экземпляр (instance) описанного класса. Когда создается объект, как правило, необходимо инициализировать его переменные. Для этого в классе определяется специальный метод или перегруженные методы, имена которых совпадают с именем класса. Эти методы называют конструкторами. Конструктор отличается от обычных методов следующими основными особенностями (другие особенности конструкторов будут рассмотрены позднее): конструктор не должен возвращать никакого значения; тип возвращаемого значения для конструктора не указывается (но можно указать модификатор доступа public или private). Если конструктор в классе не определен, компилятор Java создает конструктор по умолчанию, который для рассмотренного выше объявления класса не выполняет никаких действий. Создание объекта выполняется с помощью предложения, имеющего следующий формат: идентификатор-переменной = new идентификатор-конструктора(параметры); где идентификатор-переменной – имя создаваемого объекта, идентификаторконструктора – имя вызываемого конструктора класса, параметры – список параметров, передаваемых конструктору класса. Примеры создания объектов: obj1 = new MyClass(); obj2 = new MyClass(12, 5); Для создания объекта obj1 вызывается конструктор класса MyClass без параметров, а для создания объекта obj2 вызывается другой конструктор этого же класса, которому в качестве аргумента передаются два целых числа. Операции объявления и инициализации переменных типа класса могут быть объединены в одном операторе, например: MyClass obj3 = new MyClass(8, 4); 2.1.11.2. Переменные и методы объекта Как уже говорилось, в описании класса определяются переменные и методы, используемые в данном классе. При создании объекта переменные в разных объектах класса имеют одинаковое имя, но могут иметь разные значения, причем изменение переменной в одном экземпляре никак не влияет на значение той же переменной в другом экземпляре, поскольку в каждом объекте для этих переменных выделяется своя область памяти. Хотя под методы класса память выделяется только один раз, но каждый объект класса обращается к методам класса независимо от других объектов. Поэтому такие переменные и методы называются переменными и методами экземпляра класса (instance variables and methods) или переменными и методами объекта. Обращение к переменным и вызов методов объекта в Java имеет следующий формат: Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 21 Прикладное программирование в ТС Лекция 3-02 имя-объекта.имя-переменной и имя-объекта.имя-метода(аргументы) где имя-объекта – это идентификатор переменной объекта класса, имя-переменной – идентификатор переменной класса, имя-метода – идентификатор метода класса, а аргументы – значения, задаваемые при вызове метода. Например, если в классе определена переменная var1 типа int, то определение ее значения в объекте obj1 и obj2 можно выполнить с помощью следующего оператора: int var1InObj1 = obj1.var1; Изменение значения переменной var1 для объекта obj2 можно выполнить с помощью следующего оператора: obj2.var1 = 12; Вызов метода setVar1, определенного в классе MyClass и устанавливающего значение переменной var1 для объектов obj1 и obj2, выполняется с помощью следующих операторов: obj1.setVar1(2); obj2.setVar1(2); Если имя объекта для переменной или метода не задано, то компилятор Java считает, что данная переменная или метод определены в данном классе. Можно в этом случае вместо имени объекта указать ключевое слово this. Обычно такое указание используется в тех случаях, если имя переменной класса и аргумент метода в классе совпадают, например: class MyClass { int var1; … void setVar1(int var1) { this.var1=var1; } } В этом примере переменной класса var1 присваивается значение параметра var1, определенного в методе setVar1(). Если бы параметр в методе имел другое имя, например, var, ключевое слово this можно было не указывать и оператор присваивания можно было бы записать в виде: var1 = var; 2.1.11.3. Переменные и методы класса Модификатор static задает переменную, общую для всех объектов данного класса. Обычно для каждой генерации объекта переменным класса выделяется новая память, но когда эти элементы класса объявляются с модификатором static, им выделяется место только один раз, независимо от того, сколько объектов было создано. Так, объявленную в классе MyClass переменную static int objectCount; можно использовать для подсчета сгенерированных объектов класса MyClass. Для работы со статическими переменными обычно создаются статические методы, помеченные модификатором static. Статические методы и переменные называют также методами и переменными класса (class variables and methods), поскольку к ним можно обращаться, указывая не имя Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 22 Прикладное программирование в ТС Лекция 3-02 объекта, а имя класса. Например, пусть переменная var2 и метод setVar2 определены в классе MyClass как статические: static int var2 = 0; … static void setVar2(int var) { var2 = var; } Тогда обращение к этой переменной и методу будет иметь в программе следующий вид: int val2Value = MyClass.var2; MyClass.setVar2(12); Статические переменные инициализируются еще до начала работы конструктора, но при инициализации можно использовать только константные выражения, как это сделано выше для переменной var2. Если же инициализация требует сложных вычислений, например, циклов для задания значений элементам статических массивов или обращений к методам, то эти вычисления заключают в блок, помеченный словом static, который тоже будет выполнен до запуска конструктора, например: static int var2; static { setVar2(0); } Основная особенность статических переменных и методов: доступ к ним выполняется, даже если не создан ни один экземпляр класса. Кроме того, для статических методов действуют следующие основные ограничения: в статическом методе нельзя использовать ссылки this; в статическом методе нельзя обращаться к нестатическим переменным (т.е. все переменные, объявленные вне статического метода и используемые внутри него, должны быть объявлены с модификатором static); в статическом методе нельзя прямо вызывать нестатические методы (т.е. все методы, вызываемые из статического метода, должны быть объявлены с модификатором static). Последние два условия можно обойти, если обращаться к переменным и методам объекта. Так, если внутри статического метода setVar2 есть прямое обращение к нестатической переменной var1, то это вызовет сообщение об ошибке, однако обращение к var1 как к переменной объекта obj1 – obj1.var1 ошибки не вызовет. 2.1.11.4. Операции над объектами Объекты в языке Java могут объединяться с помощью следующих знаков операций: присваивание "=" – присвоение указателя на объект ссылочной переменной (при этом новой копии объекта не создается); проверка на равенство "==" и на неравенство "!=" – результатом этих операций будет true или false, в зависимости от того, указывают ли сравниваемые переменные на один и тот же объект в памяти. Кроме того, для объектов определена операция имя-объекта instanceof имя-класса Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 23 Прикладное программирование в ТС Лекция 3-02 Результатом этой операции является true, если объект с идентификатором имяобъекта является реализацией класса с идентификатором имя-класса, и false – в противном случае. Примеры операций над объектами: MyClass obj4 = new MyClass(15, 7); MyClass obj5 = new MyClass(15, 7); MyClass obj6; Obj6 = obj4; boolean isEqual1, isEqual2, isInstance; // isEqual1 - true (ссылки на obj4 и obj6 // указывают на один и тот же объект) isEqual = obj6 == obj4; // isEqual2 - false (obj4 и obj5 имеют одинаковые // характеристики, но указывают на разные объекты) isEqual2 = obj4 == obj5; // isInstance - true (obj4 является объектом // класса MyClass) isInstance = obj4 instanceof MyClass; 2.1.11.5. Переменные перечислимого типа Отдельным типом ссылочных переменных являются переменные перечислимого типа, определенные в спецификации языка Java 3.0 и реализованные в Java SE 5 и 6. Эти переменные определяются с помощью ключевого слова enum. Простое объявление перечислимой переменной имеет следующий вид: модификаторы-класса enum идентификатор-класса { имя-константы-1, …, имя-константы-n } где список имен констант, записываемых заглавными буквами, задает значения, которые может принимать переменная идентификатор-класса. Пример объявления и использования перечислимой переменной: Объявление переменной (как отдельного класса): // Объявление месяцев года // как перечислимой переменной Month public enum Month { JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER } Использование этой переменной в другом классе: Month currentMonth; currentMonth = Month.MARCH; В отличие от других языков объявление перечислимой переменной является объявлением класса и может иметь более сложный вид: модификаторы-класса enum идентификатор-класса { имя-константы-1(список-аргументов), …, имя-константы-n(список-аргументов) ; тело-класса Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 24 Прикладное программирование в ТС Лекция 3-02 } Список-аргументов задает отделяемые друг от друга запятыми аргументы, которые передаются конструктору класса при создании объекта данного класса. В теле класса, помимо конструктора, могут быть определены и другие методы для работы с переменными объекта. Пример объявления и использования перечислимой переменной с аргументами и телом класса: Переопределим переменную перечислимого типа Month, добавив для каждого месяца два аргумента и определив конструктор и два метода – для получения индекса метода и количества дней в месяце: public enum Month { JANUARY(1,31), FEBRUARY(2,28), MARCH(3,31), APRIL(4,30), MAY(5,31), JUNE(6,30), JULY(7,31), AUGUST(8,31), SEPTEMBER(9,30), OCTOBER(10,31), NOVEMBER(11,30), DECEMBER(12,31); // Индекс месяца private int monthIndex; // Количество дней в месяце private int numberOfDaysInMonth; // Конструктор класса Month Month(int monthIndex, int numberOfDaysInMonth){ this.monthIndex = monthIndex; this.numberOfDaysInMonth = numberOfDaysInMonth; } // Метод получения индекса месяца int getMonthIndex() { return monthIndex; } // Метод получения количества дней в месяце int getNumberOfDaysInMonth() { return numberOfDaysInMonth; } } Использование этой переменной в другом классе: Month currentMonth; currentMonth = Month.MARCH; int currentMonthIndex = currentMonth.getMonthIndex(); int currentNumberOfDaysInMonth = currentMonth.getNumberOfDaysInMonth(); В результате выполнения этого фрагмента программы переменной currentMonthIndex будет присвоено значение 3, а переменной currentNumberOfDaysInMonth – значение 31. 2.1.12. Массивы в Java Массивом в языках программирования называется именованный набор переменных одинакового типа. Каждый элемент массива является переменной, которая однозначно определяется путем указания имени массива и целочисленной позиции элемента в массиве – индекса элемента в массиве. Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 25 Прикладное программирование в ТС Лекция 3-02 2.1.12.1. Описание массивов Массивы в Java являются гибридом объектов и примитивных типов. Они похожи на объекты, но имеют специальное значение для компилятора. Описание производится в три этапа. На первом этапе выполняется объявление массива (array declaration). Объявление массива в Java выполняется с использованием одного из следующих форматов: имя-типа [] идентификатор-массива либо имя-типа идентификатор-массива[] где имя-типа – имя примитивного или ссылочного типа, а идентификатор-массива – имя, присваиваемое массиву. Пример объявления массива: int [] myArray; либо int myArray[]; Если несколько массивов имеют одинаковый тип, их также можно объявить в списке, используя первый формат записи, например: short [] a, b; На втором этапе, этапе определения или создания массива (array instantiation) указывается количество элементов массива, называемое его длиной, выделяется место для массива в оперативной памяти, переменная-ссылка получает адрес массива. Эти действия выполняются следующей операцией: new имя-типа [размер-массива] где размер-массива – переменная или выражение любого целочисленного типа, за исключением типа long. Как видно из этой операции, в отличие от определения объекта класса, для массива указывается не конструктор класса, а тип массива и его размер. Примеры определения массивов: myArray = new int[5]; byte dim1 = 10, dim2 = 2; a = new short[dim1]; b = new short[dim1 + dim2]; На третьем этапе производится инициализация массива (array initialization). Элементы массивов числовых типов получают нулевое значение соответствующего типа, элементы булевских массивов – значение false, а элементы массивов ссылочных типов – значения null. Массивы могут быть инициализированы во время создания. При этом размермассива в квадратных скобках не указывается, а после закрывающей квадратной скобки в фигурных скобках задается список значений элементов массива, разделенных запятыми. Размер массива при этом будет равен количеству элементов в списке (в списке не должно быть пустых элементов). Так, в результате выполнения оператора: myArray = new int[]{0, 1, 2, 3, 4}; массив будет иметь размер 5, и состоять из элементов, заданных в списке. Можно объединить первый и второй этапы в одном операторе, например: int [] myArray = new int[5]; Поскольку при инициализации массива размер массива указывать не надо, можно объединить первый и третий этап в одном операторе, например: int [] myArray = {0, 1, 2, 3, 4}; Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 26 Прикладное программирование в ТС Лекция 3-02 2.1.12.2. Доступ к элементам массива Элемент массива, помимо значения, характеризуется своей позицией в массиве – индексом. Индексом элемента является значение типа byte, short, int или char, заключенное в квадратные скобки, например myArray[4]. Индекс элемента массива изменяется от нуля до величины, на единицу меньшей размера массива. Так, значения элементам объявленного выше массива a можно присвоить с помощью следующих операторов: a[0] = 24; // Значение первого элемента массива a[1] = -7; // Значение второго элемента массива … a[9] = 4; // Значение последнего, десятого // элемента массива Длину массива можно определить с помощью свойства массива length, например, длину определенного выше массива myArray можно определить с помощью следующего оператора: int myArrayLength = myArray.length; В результате выполнения этого оператора переменная myArrayLength получит значение 5. 2.1.12.3. Многомерные массивы У массивов в Java должна быть указана, по крайней мере, одна размерность, однако в программах могут использоваться и многомерные массивы, причем остальные размерности можно определять во время выполнения программы. Элементами массивов в Java могут быть другие массивы. Например, объявление двумерной матрицы может выглядеть следующим образом: int [][] matrix = new int[3][4]; Первый (левый) размер массива должен задаваться при его создании. Другие размеры могут указываться позже. Использование более чем одной размерности является сокращением для вложенного набора операторов new. Таким образом, можно создавать непрямоугольные массивы, например: int [] [] triangleMatrix = new int [3] [] ; triangleMatrix[0] = new int [1]; triangleMatrix[0][0] = 1; triangleMatrix[1] = new int [2]; triangleMatrix[1][0] = 2; triangleMatrix[1][1] = 3; triangleMatrix[2] = new int [3]; triangleMatrix[2][0] = 4; triangleMatrix[2][1] = 5; triangleMatrix[2][2] = 6; Матрица triangleMatrix будет треугольной, и будет иметь следующий вид: 1 2 3 4 5 6 2.1.13. Операторы передачи управления в Java При работе программы операторы выполняются последовательно. Однако часто бывает необходимым изменить порядок выполнения операторов. Для этого используются операторы управления программой. Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 27 Прикладное программирование в ТС Лекция 3-02 В Java существуют следующие виды операторов управления программы: условный оператор; операторы цикла; операторы перехода; оператор выбора. 2.1.13.1. Условный оператор Условный оператор Java имеет следующий формат: if (булевское-выражение) операторы-1; else операторы-2; Если значение булевского-выражения равно true, то выполняются операторы-1, иначе выполнятся операторы-2. Оператор else может быть опущен, в этом случае, если булевское-выражение равно false, выполняется оператор, следующий за оператором if. Если операторы-1 или операторы-2 содержат несколько операторов, они должны быть заключены в фигурные скобки. Пример оператора if: if (x == 0) y = 0; else { y = 1; z = x + 1; } 2.1.13.2. Операторы цикла В Java определены следующие операторы цикла: оператор for; расширенный оператор for; оператор while; оператор do…while. Оператор цикла for имеет следующий формат: for (инициализация-цикла;контрольное-выражение;шаговое-выражение) тело-цикла Инициализация-цикла выполняется один раз перед первым проходом тела цикла. Контрольное-выражение вычисляется перед началом каждого выполнения тела цикла (в том числе и перед первым его выполнением), а шаговое-выражение вычисляется в конце каждого выполнения тела цикла. Как правило, в нем происходит увеличение или уменьшение некоторой переменной, от которой зависит значение контрольного выражения. Тело цикла может содержать один оператор или несколько операторов, заключенных в фигурные скобки. Пример оператора for (суммирование элементов массива): int a[] = {1, 2, 3, 4, 5}; int sum = 0; for (int i = 0; i < a.length; i++) Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 28 Прикладное программирование в ТС Лекция 3-02 sum+=a[i]; В результате выполнения цикла переменной sum будет присвоено значение 15. В выражение для инициализации цикла и в выражение для итерации цикла можно включить несколько операторов, разделенных запятыми. Обычно такая форма применяется, когда в цикле необходимо использовать несколько переменных цикла. Так, приведенный пример проверяет, является ли массив a симметричным, т.е. значение первого элемента равно значению последнего элемента массива, значение второго элемента – значению предпоследнего элемента и т.д. boolean isSymmetric = true; … for(int i=0, j=a.length-1; i < j; i++, j--) if(a[i] != a[j]) isSymmetric = false; Если массив a является симметричным, значение isSymmetric не изменится, в противном случае эта переменная получит значение false. Расширенный оператор for, определенный в спецификации языка Java 3.0 и реализованный в Java SE 5 и 6, имеет следующий формат: for(модификаторы-переменной тип идентификатор-переменной : выражение) тело-цикла Выражение в этом цикле должно задавать набор значений переменной. Если набор значений является массивом, в качестве выражения задается имя массива. Набор элементов переменной перечислимого типа и коллекций (коллекции будут рассмотрены далее) можно получить с помощью статического метода values(). В процессе выполнения цикла переменной с именем идентификатор-переменной будут последовательно присваиваться значения всех элементов массива, переменной перечислимого типа или коллекции. Пример расширенного оператора for для массива (суммирование элементов массива): int a[] = {1, 2, 3, 4, 5}; int sum = 0; for (int ai : a) sum+=ai; Пример расширенного оператора for для переменной перечислимого типа (определение количества дней в году). Для получения количества дней в году используется переменная перечислимого типа Month с аргументами, определенная в разд. 1.10.5. int dayNumber = 0; for (Month monthN : Month.values()) dayNumber+=monthN.getNumberOfDaysInMonth(); В результате выполнения этого фрагмента переменной dayNumber будет присвоено значения 365. Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 29 Прикладное программирование в ТС Лекция 3-02 Операторы цикла while и do…while имеют следующий формат: while(булевское-выражение) do тело-цикла тело-цикла while(булевское-выражение); Оба цикла выполняются до тех пор, пока булевское-выражение имеет значение true, однако в цикле while вычисление булевского-выражения производится до начала очередного выполнения цикла, а в цикле do…while – после его очередного выполнения (т.е. цикл do…while будет всегда выполняться хотя бы один раз). Пример оператора цикла while: int a[] = {1, 2, 3, 4, 5}; int sum = 0, i = 0; while (i < a.length) { sum+=a[i]; i ++; } Сравнение выполнения операторов while и do…while: int a[] = {1, 2, 3, 4, 5}; int int a[] = {1, 2, 3, 4, 5}; int sum = 0, i = 0; sum = 0, i = 0; while (i < 0) do { { sum+=a[i]; sum+=a[i]; i ++; i ++; } } while (i < 0); В первом случае цикл не будет выполнен ни разу и значения переменных sum и i будут равны 0, во втором случае цикл будет выполнен один раз и значения переменных sum и i будут равны 1. 2.1.13.3. Операторы перехода В Java отсутствует оператор goto метка языка C, позволяющий выполнять переход к оператору с указанной меткой, поскольку этот оператор может существенно усложнить логическую структуру программы. В тех же случаях, когда необходимо пропустить выполнение некоторых операторов программы, в Java используются оператор прерывания break и оператор продолжения continue. К операторам перехода обычно относят и оператор return, который был рассмотрен ранее в разделе, посвященном методам. Оператор прерывания: break метка; передает управление за пределы цикла или оператора выбора, помеченного указанной меткой. Метка представляет собой обычный идентификатор Java, за которым следует двоеточие. Метка может быть опущена – в этом случае управление передается за пределы цикла или оператора выбора, содержащего данный оператор break. Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 30 Прикладное программирование в ТС Лекция 3-02 Пример использования оператора break без метки (определение индекса первого нулевого элемента в массиве): int a[] = {1, 2, 3, 0, 5}; int i, zeroIndex = -1; for (i = 0; i < a.length; i++) if(a[i] == 0) break; if (i < a.length) zeroIndex = i; Выполнение данного цикла будет прервано, при значении i, равном 3, и будет выполняться оператор if. В этом случае метку указывать нет необходимости, поскольку оператор break передает управление за пределы цикла, в котором он находится. Пример использования оператора break с меткой (определение индексов первого нулевого элемента в двумерном массиве): int a[][] = {{1,2,3},{5,6,7},{8,9,0},{11,12,13}}; int i, j; … iLoop: for(i = 0; i < 4; i++) { for(j = 0; j < 3; j++) { if (a[i][j] == 0) break iLoop; } } В данном случае необходимо прервать выполнение не только того цикла, в котором находится оператор break (цикла по j), но и внешнего по отношению к нему цикла (цикла по i), иначе выполнение внешнего цикла будет продолжено. Использование метки iLoop в операторе break позволяет прервать выполнение цикла, помеченного этой меткой (цикла по i). Оператор продолжения continue метка; похож на оператор break, однако он передает управление внутри цикла. Если метка опущена, оператор continue передает управление на самый конец тела цикла (после последнего его оператора) и, если контрольное выражение, вычисленное после того, как очередной проход тела цикла был насильственно завершен оператором continue, будет равно true, выполнение цикла продолжится. Это бывает полезно для того, чтобы пропустить при выполнении тела цикла некоторые из операторов. Если метка указана, операторы пропускаются в цикле, помеченном указанной меткой Пример использования оператора continue (суммирование элементов массива с четными значениями): int a[] = {1, 2, 3, 4, 5}; int sum = 0; for (int i = 0; i < a.length; i++) { Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 31 Прикладное программирование в ТС Лекция 3-02 if(a[i]%2 != 0) continue; sum += a[i]; } Выполнение оператора sum += a[i]; в цикле будет пропущено для тех значений элементов массива, для которых остаток при делении на 2 не равен 0 (нечетных чисел). 2.1.13.4. Оператор выбора Оператор выбора switch обычно используется, когда необходимо организовать ветвление программы по нескольким направлениям, однако условия проверки в нем должны быть выражены с помощью различных значений целой переменной. Оператор switch имеет следующий формат: switch (выражение) { case значение-1: операторы-1; case значение-2: операторы-2; … default: операторы-n; } Выражение, которое ставится в круглых скобках после ключевого слова switch, может принадлежать к одному из типов char, byte, short или int, а также одним из значений переменной перечислимого типа. Проверка значения этого выражения на равенство заданному константному значению осуществляется с помощью операторов case, входящих в оператор switch (все операторы case должны содержать различные значения). Как только один из операторов case опознал значение выражения, управление получает операторы, следующие после символа ":" за этим оператором case. После этого выполняются операторы, содержащиеся во всех следующих операторах case и в операторе default. Для того, чтобы обойти выполнение последующих операторов case и оператора default, следует задать последним оператором для данного case оператор break. Если значение выражения не совпадает ни с одним из значений, выполняются операторы, следующие за оператором default (этот оператор может быть опущен). Операторы, следующие за двоеточием, можно не заключать в фигурные скобки. Если для нескольких значений выполняются одни и те же действия, операторы case можно объединить: case значение-1: case значение-2: операторы; Пример оператора выбора: char s; int x; switch(s) { case 'a': x = 0; break; case 'A': x = 1; break; Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А. - 32 Прикладное программирование в ТС Лекция 3-02 case ' ': case '(': case ')': x = 2; break; default: x = 3; } Файл: 681465783 Создан: 09.07.2007 Модифицирован: 01.05.2016 Автор: Шонин В.А.