Java: руководство для начинающих [9-е изд.] 9781260463552, 9786175501009

В книге раскрыты основы и кратко описаны расширенные функциональные средства языка Java, в числе которых многопоточное п

156 74 16MB

Russian (Old) Pages 752 [753] Year 2023

Report DMCA / Copyright

DOWNLOAD FILE

Polecaj historie

Java: руководство для начинающих [9-е изд.]
 9781260463552, 9786175501009

Table of contents :
Содержание
Предисловие
Глава 1. Основы языка Java
Глава 2. Введение в типы данных и операции
Глава 3. Операторы управления программой
Глава 4. Введение в классы, объекты и методы
Глава 5. Дополнительные сведения о типах данных и операциях
Глава 6. Дополнительные сведения о методах и класса
Глава 7. Наследование
Глава 8. Пакеты и интерфейсы
Глава 9. Обработка исключений
Глава 10. Использование ввода-вывода
Глава 11. Многопоточное программирование
Глава 12. Перечисления, автоупаковка, аннотации и многое другое
Глава 13. Обобщения
Глава 14. Лямбда-выражения и ссылки на методы
Глава 15. Модули
Глава 16. Выражения switch, записи и прочие недавно добавленные средства
Глава 17. Введение в Swing
Приложение А. Ответы на вопросы и решения упражнений для самопроверки
Приложение Б. Использование документирующих комментариев Java
Приложение В. Компиляция и запуск простых однофайловых программ за один шаг
Приложение Г. Введение в JShell
Приложение Д. Дополнительные ключевые слова Java
Предметный указатель

Citation preview

Java:

руководство для начинающих 9-е издание

А

Jаvатм

Beginner's Guide Ninth Edition Herbert Schildt

Milan

New York Chicago San Francisco Athens London Madrid Mexico City New Delhi Singapore Sydney Toronto

Java:

руководство для начинающих 9-е издание Герберт Шилдт

Киiв Видавництво "НАУКОВИЙ СВIТ"

2023

УДК ОО4. 432 Ш57 Перевод с английского и редакция Ю.Н. Артеменко

Шилдт, Г.

Java: руководство для начинающих, 9-е изд. /Герберт Шилдт ; Ш57 пер. с англ. Ю. Н. Артеменко. - Киев. : "Науковий Св iт ': 2023. 752с. : ил. - Парал. тит. англ. ISBN ISBN

978-617- 550-100- 9 (укр.) 978- 1- 260- 46355- 2 (англ.)

В книге раскрыты основы и кратко описаны расширенные функциональные средства языка Java, в числе которых многопоточное программирование, обобще­ ния, лямбда-выражения и графический интерфейс Swing. Вдобавок приводится четкое объяснение перечислений, модулей и методов интерфейса. В этом руко­ водстве предлагается эффективное сочетание теории и практики написания кода, которое позволит быстро приступить к разработке приложений на языке Java. Книга предназначена для программистов, желающих изучить язык Java, и для разработчиков веб-приложений, которые стремятся повысить уровень знаний и мастерства.

УДКОО4.432

Все названия программных продуктов являются зарегистрированными торговыми мар ­ ками соответствующих фирм. Все права защищены. Никакая часть настоящего издани я ни в каких целях не может быть воспрои зведена в какой бы то ни было форме и какими бы то ни было средствами, будь то электронные или механические, включая фотокопирование и запись на магнитный носитель, если на это нет письменного разрешени я издательства McGraw Hill. Copyright ©

2022 Ьу McGraw Hill.

АН rights reserved. АН trademarks are trademarks of their respective owners. Oracle Corporation does not make any representations or warranties as to the accuracy, ade ­ quacy, or completeness of any information contained in this Work, and is not responsiЬ!e for any errors or omissions. Authorized translation from the English language edition of the fava: А

Edition (ISBN 978-1-260-46355-2), puЬ!ished Ьу McGraw Hill.

Except as permitted under the United States Copyright Act of

Beginner's Guide, 9th

1976, по part of this puЬ!ication

may Ье reproduced or distributed in any form or Ьу any means, or stored in а database or retrieval system, without the prior written permission of the puЬlisher. ISBN ISBN

978-617-550-100-9 (укр.) 978-1-260-46355-2 (англ.)

© ООО "Науковий Свiт': перевод,

©

2022 Ьу McGraw Hill

2023

О гла вл ен и е Предисловие

19

Глава 1. Основы языка Java

27

Глава 2. Введение в типы данных и операции

65

Глава 3. Операторы управления проrраммой

97

Глава 4. Введение в классы, объекты и методы

135

Глава 5. Дополнительные сведения о типах данных и операциях

165

Глава 6. Дополнительные сведения о методах и классах

217

Глава 7. Наследование

259

Глава 8. Пакеты и интерфейсы

30 1

Глава 9. Обработка исключений

335

Глава 1 О. Использование ввода-вывода

365

Глава 1 1. Мноrопоточное проrраммирование

41 1

Глава 12. Перечисления, автоупаковка, аннотации и мноrое друrое

453

Глава 13. Обобщения

487

Глава 14. Лямбда-выражения и ссылки на методы

523

Глава 15. Модули

557

Глава 16. Выражения switch, записи и прочие недавно добавленные средства

589

Глава 17. Введение в Swing

623

Приложение А. Ответы на вопросы и решения упражнений для самопроверки

66 1

Приложение Б. Использование документирующих комментариев Java

715

Приложение В. Компиляция и запуск простых однофайловых проrрамм за один шаr

725

Приложение Г. Введение в JShell

729

Приложение Д. Дополнительные ключевые слова Java

74 1

Предметный указатель

746

Содержание Об авторе

18

Предисловие

19 Эволюция языка Java 19 Структура книги 25 Ключевые навыки и концепции 25 Вопросы и упражнения для самопроверки 25 Спросим у эксперта 25 Упражнения 25 Предыдущий опыт программирования не требуется 25 Необходимое программное обеспечение 25 Исходный код примеров 26 Благодарности 26 Ждем ваших отзывов! 26

Глава 1. Основы языка Java

27 История и философия языка Java 29 Происхождение Java 29 Родословная Java: C и C++ 30 Влияние Java на Интернет 31 Магия Java: байт-код 34 Выход за рамки аплетов 36 Более быстрый график выпуска 37 Характеристики языка Java 38 Объектно-ориентированное программирование 39 Инкапсуляция 40 Полиморфизм 41 Наследование 41 Комплект разработчика на Java 42 Первая простая программа 43 Ввод исходного кода программы 44 Компиляция программы 44 Подробный анализ первого примера программы 45 Обработка синтаксических ошибок 48 Вторая простая программа 49 Другие типы данных 51 Два управляющих оператора 54 Оператор if 54 Цикл for 55 Создание блоков кода 57

Содержание Точки с запятой и размещение операторов Практика отступов Ключевые слова Java Идентификаторы в Java Библиотеки классов Java

7 58 59 61 62 62

Глава 2. Введение в типы данных и операции

65 Важность типов данных 66 Примитивные типы Java 67 Целые числа 67 Типы с плавающей точкой 69 Символы 70 Тип boolean 71 Литералы 73 Шестнадцатеричные, восьмеричные и двоичные литералы 74 Управляющие последовательности символов 74 Строковые литералы 75 Подробный анализ переменных 76 Инициализация переменной 77 Динамическая инициализация 77 Область видимости и время жизни переменных 78 Операции 81 Арифметические операции 81 Инкремент и декремент 82 Операции отношения и логические операции 83 Короткозамкнутые логические операции 85 Операция присваивания 86 Сокращенные операции присваивания 88 Преобразование типов при присваивании 88 Приведение несовместимых типов 90 Старшинство операций 91 Выражения 93 Преобразования типов в выражениях 94 Использование пробельных символов и круглых скобок 95

Глава 3. Операторы управления программой Ввод символов с клавиатуры Оператор if Вложенные операторы if Цепочка if-else-if Традиционный оператор switch Вложенные операторы switch Цикл for

97 98 100 101 102 103 107 109

8

Java: руководство для начинающих, 9-е издание Некоторые разновидности цикла for Пропуск частей Бесконечный цикл Циклы без тела Объявление переменных управления циклом внутри цикла for Расширенный цикл for Цикл while Цикл do-while Использование оператора break для выхода из цикла Использование оператора break как разновидности перехода в стиле “goto” Использование оператора continue Вложенные циклы

111 112 113 113 114 115 115 116 121 123 127 132

Глава 4. Введение в классы, объекты и методы

135 Основы классов 136 Общая форма класса 137 Определение класса 138 Создание объектов 141 Ссылочные переменные и присваивание 142 Методы 142 Добавление метода в класс Vehicle 143 Возврат из метода 145 Возврат значения 146 Использование параметров 148 Добавление параметризованного метода в класс Vehicle 150 Конструкторы 157 Параметризованные конструкторы 158 Добавление конструктора в класс Vehicle 159 Еще раз об операции new 160 Сборка мусора 160 Ключевое слово this 161

Глава 5. Дополнительные сведения о типах данных и операциях

165 Массивы 166 Одномерные массивы 167 Многомерные массивы 172 Двумерные массивы 172 Массивы нестандартной формы 173 Массивы с тремя и более измерениями 175 Инициализация многомерных массивов 175 Альтернативный синтаксис объявления массивов 176 Присваивание ссылок на массивы 176

Содержание

9

Использование члена массива length 177 Цикл for в стиле “for-each” 183 Проход по многомерным массивам 186 Применение расширенного цикла for 188 Строки 188 Конструирование строк 189 Оперирование строками 190 Массивы строк 192 Строки являются неизменяемыми 192 Использование строки для управления оператором switch 193 Использование аргументов командной строки 196 Использование выведения типов для локальных переменных 197 Использование выведения типов локальных переменных для ссылочных типов 199 Использование выведения типов локальных переменных в цикле for 201 Некоторые ограничения var 202 Побитовые операции 203 Побитовые операции И, ИЛИ, исключающее ИЛИ и НЕ 203 Операции сдвига 208 Составные побитовые операции присваивания 210 Операция ? 213

Глава 6. Дополнительные сведения о методах и классах

217 Управление доступом к членам класса 218 Модификаторы доступа Java 219 Передача объектов методам 225 Способы передачи аргументов 226 Возвращение объектов 228 Перегрузка методов 230 Перегрузка конструкторов 235 Рекурсия 239 Ключевое слово static 242 Статические блоки 244 Введение во вложенные и внутренние классы 248 Аргументы переменной длины 251 Основы аргументов переменной длины 252 Перегрузка методов с аргументами переменной длины 254 Аргументы переменной длины и неоднозначность 256

Глава 7. Наследование Основы наследования Доступ к членам и наследование Конструкторы и наследование

259 260 263 265

10

Java: руководство для начинающих, 9-е издание Использование ключевого слова super для вызова конструкторов суперкласса 267 Использование ключевого слова super для доступа к членам суперкласса 271 Создание многоуровневой иерархии 274 Когда конструкторы выполняются? 277 Ссылки на суперклассы и объекты подклассов 278 Переопределение методов 282 Переопределенные методы поддерживают полиморфизм 285 Зачем нужны переопределенные методы? 287 Применение переопределения методов к классу TwoDShape 287 Использование абстрактных классов 290 Использование ключевого слова final 294 Использование ключевого слова final для предотвращения переопределения 294 Использование ключевого слова final для предотвращения наследования 295 Использование ключевого слова final с членами данных 295 Класс Object 297

Глава 8. Пакеты и интерфейсы

301 Пакеты 302 Определение пакета 303 Поиск пакетов и CLASSPATH 304 Краткий пример пакета 305 Пакеты и доступ к членам классов 306 Пример доступа к пакету 307 Защищенные члены 309 Импортирование пакетов 311 Библиотека классов Java содержится в пакетах 312 Интерфейсы 313 Реализация интерфейсов 315 Использование ссылок на интерфейсы 318 Переменные в интерфейсах 324 Интерфейсы можно расширять 325 Стандартные методы интерфейса 326 Основы стандартных методов 327 Более реалистичный пример стандартного метода 329 Проблемы множественного наследования 330 Использование статических методов в интерфейсе 331 Закрытые методы интерфейса 332 Заключительные соображения по поводу пакетов и интерфейсов 333

Содержание

Глава 9. Обработка исключений Иерархия исключений Основы обработки исключений Использование try и catch Простой пример обработки исключений Последствия от неперехваченных исключений Исключения позволяют изящно обрабатывать ошибки Использование нескольких операторов catch Перехват подклассов исключений Блоки try могут быть вложенными Генерация исключений Повторная генерация исключений Подробный анализ класса Throwable Использование finally Использование throws Три дополнительных средства в системе исключений Встроенные исключения Java Создание подклассов Exception

Глава 10. Использование ввода-вывода

11 335 337 337 338 339 341 342 343 344 346 347 348 349 350 352 354 355 358

365 Ввод-вывод в Java основан на потоках 367 Потоки байтовых и символьных данных 367 Классы потоков байтовых данных 367 Классы потоков символьных данных 369 Предопределенные потоки 370 Использование потоков байтовых данных 370 Чтение консольного ввода 372 Запись консольного вывода 373 Чтение и запись в файлы с использованием потоков байтовых данных 374 Ввод из файла 375 Вывод в файл 379 Автоматическое закрытие файла 380 Чтение и запись двоичных данных 384 Файлы с произвольным доступом 388 Использование потоков Java, основанных на символах 390 Консольный ввод с использованием потоков символьных данных 392 Консольный вывод с использованием потоков символьных данных 396 Файловый ввод-вывод с использованием потоков символьных данных 398 Использование FileWriter 398 Использование FileReader 399 Использование оболочек типов Java для преобразования числовых строк 400 Вопросы и упражнения для самопроверки 410

12

Java: руководство для начинающих, 9-е издание

Глава 11. Многопоточное программирование

411 Основы многопоточности 412 Класс Thread и интерфейс Runnable 414 Создание потока 414 Одно улучшение и два простых изменения 418 Создание нескольких потоков 425 Выяснение, завершен ли поток 427 Приоритеты потоков 430 Синхронизация 433 Использование синхронизированных методов 434 Оператор synchronized 437 Взаимодействие между потоками с использованием notify(), wait() и  notifyAll() 439 Пример использования wait() и notify() 441 Приостановка, возобновление и останов потоков 446

Глава 12. Перечисления, автоупаковка, аннотации и многое другое

453 Перечисления 455 Основы перечислений 455 Перечисления Java являются типами классов 458 Методы values() и valueOf() 458 Конструкторы, методы, переменные экземпляра и перечисления 459 Два важных ограничения 461 Перечисления унаследованы от Enum 461 Автоупаковка 468 Оболочки типов 469 Основы автоупаковки 471 Автоупаковка и методы 472 Автоупаковка/автораспаковка и выражения 473 Предостережение 475 Статическое импортирование 475 Аннотации (метаданные) 479 Введение в операцию instanceof 482

Глава 13. Обобщения Основы обобщений Простой пример обобщения Обобщения работают только со ссылочными типами Обобщенные типы различаются на основе их аргументов типов Обобщенный класс с двумя параметрами типов Общая форма обобщенного класса

487 489 490 493 494 494 495

Содержание

13

Ограниченные типы 495 Использование аргументов с подстановочными знаками 499 Ограниченные аргументы с подстановочными знаками 501 Обобщенные методы 503 Обобщенные конструкторы 506 Обобщенные интерфейсы 507 Низкоуровневые типы и унаследованный код 513 Выведение типов с помощью ромбовидной операции 516 Выведение типов локальных переменных и обобщения 518 Стирание 518 Ошибки неоднозначности 519 Некоторые ограничения обобщений 520 Невозможность создать экземпляры параметров типов 520 Ограничения, касающиеся статических членов 520 Ограничения, касающиеся обобщенных массивов 520 Ограничения, касающиеся обобщенных исключений 521 Продолжение изучения обобщений 521

Глава 14. Лямбда-выражения и ссылки на методы Введение в лямбда-выражения Основы лямбда-выражений Функциональные интерфейсы Лямбда-выражения в действии Блочные лямбда-выражения Обобщенные функциональные интерфейсы Лямбда-выражения и захват переменных Генерация исключений в лямбда-выражениях Ссылки на методы Ссылки на статические методы Ссылки на методы экземпляра Ссылки на конструкторы Предопределенные функциональные интерфейсы

Глава 15. Модули Основы модулей Простой пример модуля Компиляция и запуск первого примера модуля Более подробный анализ операторов requires и exports Модуль java.base и модули платформы Унаследованный код и неименованные модули Экспортирование пакета для указанного модуля Использование requires transitive

523 525 525 527 529 533 535 541 542 544 544 546 550 552 557 559 560 565 566 567 569 570 571

14

Java: руководство для начинающих, 9-е издание Использование служб Основы служб и поставщиков служб Ключевые слова, связанные со службами Пример службы, основанной на модулях Дополнительные характеристики модулей Открытые модули Оператор opens Оператор requires static Продолжение изучения модулей

576 576 577 578 584 584 585 585 585

Глава 16. Выражения switch, записи и прочие недавно добавленные средства

589 Расширения оператора switch 591 Использование списка констант case 593 Появление выражения switch и оператора yield 594 Появление стрелки в операторе case 596 Подробный анализ оператора case со стрелкой 598 Записи 605 Основы записей 605 Создание конструкторов записи 608 Подробный анализ методов получения для записи 613 Сопоставление с образцом в операции instanceof 613 Запечатанные классы и интерфейсы 616 Запечатанные классы 616 Запечатанные интерфейсы 618 Будущие направления развития 620

Глава 17. Введение в Swing

623

Происхождение и философия, положенная в основу проектного решения Swing 625 Компоненты и контейнеры 627 Компоненты 628 Контейнеры 628 Панели контейнеров верхнего уровня 629 Диспетчеры компоновки 629 Первая простая программа Swing 630 Анализ первой простой программы Swing 632 Обработка событий в Swing 636 События 636 Источники событий 636 Прослушиватели событий 637 Классы событий и интерфейсы прослушивателей 637

Содержание Использование JButton Работа с JTextField Создание JCheckBox Работа с JList Использование внутренних анонимных классов или лямбда-выражений для обработки событий

Приложение А. О  тветы на вопросы и решения упражнений для самопроверки Глава 1. Основы языка Java Глава 2. Введение в типы данных и операции Глава 3. Операторы управления программой Глава 4. Введение в классы, объекты и методы Глава 5. Дополнительные сведения о типах данных и операциях Глава 6. Дополнительные сведения о методах и классах Глава 7. Наследование Глава 8. Пакеты и интерфейсы Глава 9. Обработка исключений Глава 10. Использование ввода-вывода Глава 11. Многопоточное программирование Глава 12. П  еречисления, автоупаковка, аннотации и многое другое Глава 13. Обобщения Глава 14. Лямбда-выражения и ссылки на методы Глава 15. Модули Глава 16. Выражения switch, записи и прочие недавно добавленные средства Глава 17. Введение в Swing

Приложение Б. Использование документирующих комментариев Java Дескрипторы javadoc @author {@code} @deprecated {@docRoot} @exception @hidden {@index} {@inheritDoc} {@link} {@linkplain} {@literal} @param @provides

15 638 642 645 648 657 661 662 664 665 668 669 674 678 681 683 686 689 691 695 699 702 704 708 715 716 717 718 718 718 718 718 718 719 719 719 719 720 720

16

Java: руководство для начинающих, 9-е издание @return @see @since {@summary} @throws @uses {@value} @version Общая форма документирующего комментария Вывод утилиты javadoc Пример использования документирующих комментариев

720 720 721 721 721 721 721 722 722 722 722

Приложение В. Компиляция и запуск простых однофайловых программ за один шаг

725

Приложение Г. Введение в JShell

729 Основы JShell 730 Просмотр, редактирование и повторное выполнение кода 733 Добавление метода 734 Создание класса 735 Использование интерфейса 736 Вычисление выражений и использование встроенных переменных 737 Импортирование пакетов 738 Исключения 739 Другие команды JShell 739 Дальнейшее исследование JShell 740

Приложение Д. Дополнительные ключевые слова Java Модификаторы transient и volatile strictfp assert Собственные методы Еще одна форма this

Предметный указатель

741 742 742 743 744 744 746

Об авторе Автор многочисленных бестселлеров Герберт Шилдт занимался написанием книг по программированию на протяжении более трех десятилетий и считается признанным авторитетом по языку Java. Журнал International Developer назвал его одним из ведущих авторов книг программированию в мире. Книги Герберта Шилдта продавались миллионными тиражами по всему миру и переведены на многие языки. Он является автором многочисленных книг по Java, в том числе Java. Полное руководство; C++: методики программирования Шилдта; Искусство программирования на Java и SWING: руководство для начинающихe. Герберт Шилдт также много писал о языках C, C++ и C#. В книге Эда Бернса Secrets of the Rock Star Programmers: Riding the IT Crest указано, что как один из звездных программистов Шилдт интересуется всеми аспектами вычислений, но его основное внимание сосредоточено на языках программирования. Герберт Шилдт получил степени бакалавра и магистра в Иллинойском университете. Его сайт доступен по адресу www.HerbSchildt.com.

О научном редакторе Доктор Дэнни Ковард работал над всеми версиями платформы Java. Он руководил определением сервлетов Java в первой версии платформы Java EE и в более поздних версиях, внедрением веб-служб в Java ME, а также стратегией и планированием для Java SE 7. Он основал технологию JavaFX и совсем недавно создал крупнейшее дополнение к стандарту Java EE 7, Java WebSocket API. Доктор Дэнни Ковард обладает уникально широким взглядом на многие аспекты технологии Java. Его опыт простирается от написания кода на Java до разработки API с отраслевыми экспертами и работы в течение нескольких лет в качестве исполнительного директора в Java Community Process. Кроме того, он является автором двух книг по программированию на Java: Java WebSocket Programming и Java EE 7: The Big Picture. Совсем недавно он применил свои знания Java, помогая масштабировать крупные службы на основе Java для одной из самых успешных в мире компаний-разработчиков программного обеспечения. Доктор Дэнни Ковард получил степень бакалавра, магистра и доктора математики в Оксфордском университете.

Предисловие

Ц

ель настоящей книги — научить вас основам программирования на языке Java. В ней используется пошаговый подход с многочисленными примерами, самопроверками и проектами. В книге предполагается отсутствие у читателя предыдущего опыта программирования. Книга начинается с описания основ, таких как компиляция и запуск программы на Java. Затем обсуждаются ключевые слова, функциональные средства и конструкции, формирующие ядро языка Java. Вы также найдете описание ряда наиболее сложных средств Java, включая многопоточное программирование, обобщения, лямбда-выражения, записи и модули. Завершает книгу введение в основы Swing. Закончив изучение книги, вы будете хорошо разбираться в основах программирования на Java. Прежде всего, важно отметить, что данная книга является лишь отправной точкой. Java — нечто большее, чем просто элементы, определяющие язык. Java также включает обширные библиотеки и инструменты, помогающие разрабатывать программы. Чтобы быть первоклассным программистом на Java, необходимо освоить эти области. После прочтения книги у вас будут знания, необходимые для изучения остальных аспектов Java.

Эволюция языка Java Коренным образом изменить саму суть программирования смогли только некоторые языки. Но и в этой группе избранных один язык выделяется среди остальных, потому что его влияние было быстрым и широко распространенным. Конечно же, речь идет о Java. Не будет преувеличением сказать, что первоначальный выпуск Java 1.0 в 1995 году компанией Sun Microsystems, Inc. произвел революцию в программировании, которая радикально трансформировала Интернет в по-настоящему интерактивную среду. Вдобавок язык Java установил новый стандарт в проектировании языков программирования. На протяжении многих лет язык Java продолжал расти, развиваться и иным образом переопределять себя. В отличие от многих других языков, которые медленно внедряют новые функциональные средства, Java часто находится в авангарде разработки языков программирования. Одной из причин является культура инноваций и изменений, которая окружала Java. В результате язык Java претерпел несколько обновлений, из которых одни были относительно небольшими, а другие — более значительными.

Предисловие

19

Первым крупным обновлением Java стала версия 1.1. Возможности, добавленные в Java 1.1, оказались более существенными, чем можно было подумать из-за увеличения номера только младшей версии. Например, в Java 1.1 добавлено много новых библиотечных элементов, переопределен способ обработки событий и перенастроены многие функции исходной библиотеки версии 1.0. Следующим крупным выпуском Java был Java 2, где “2” означает “второе поколение”. Создание Java 2 стало переломным событием, положившим начало “современной эпохи” Java. Первый выпуск Java 2 имел номер версии 1.2. Может показаться странным, что для первого выпуска Java 2 использовался номер версии 1.2. Причина в том, что первоначально он относился к внутреннему номеру версии библиотек Java, но потом был обобщен для ссылки на целый выпуск. С выходом Java 2 в компании Sun перепаковали продукт Java как J2SE (Java 2 Platform Standard Edition — платформа Java 2, стандартная редакция) и номера версий стали применяться к данному продукту. Первым крупным обновлением первоначального выпуска Java 2 стала версия J2SE 1.3. По большей части она расширяла существующую функциональность и “усиливала” среду разработки. Выпуск J2SE 1.4 дополнительно улучшил Java. Он содержал несколько важных обновлений, улучшений и дополнений, в том числе цепочки исключений, подсистему ввода-вывода на основе каналов и ключевое слово assert. Выпуск J2SE 5 произвел по существу вторую революцию Java. В отличие от большинства предшествующих обновлений Java, которые предлагали важные, но умеренные улучшения, выпуск J2SE 5 фундаментальным образом расширил границы, возможности и область использования языка. Чтобы оценить значимость изменений, которые были внесены в Java в выпуске J2SE 5, взгляните на следующий список, где перечислены только основные из всех новых функциональных средств: zzобобщения; zzавтоупаковка и автораспаковка; zzперечисления; zzрасширенный цикл for в стиле “for-each”; zzаргументы переменной длины (vararg); zzстатическое импортирование; zzаннотации.

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

20

Java: руководство для начинающих, 9-е издание

Важность упомянутых выше новых средств отражена в назначенном номере версии — 5. В других условиях следующим номером версии Java был бы 1.5. Однако новые средства были настолько значительными, что переход от версии 1.4 к 1.5, казалось, просто не смог бы выразить масштаб изменений. Взамен в Sun предпочли увеличить номер версии до 5, чтобы подчеркнуть тот факт, что произошло важное событие. Таким образом, выпуск получил название J2SE 5, а комплект разработчика на Java (Java Development Kit) — JDK 5. Тем не менее, ради соблюдения согласованности в Sun решили использовать 1.5 в качестве внутреннего номера версии, который также называют номером версии разработчиков. Цифра 5 в J2SE 5 называется номером версии продукта. Следующий выпуск Java получил имя Java SE 6. В Sun снова решили изменить название платформы Java. Прежде всего, обратите внимание, что цифра 2 была отброшена. Соответственно, платформа стала называться Java SE, а официальный продукт — Java Platform, Standard Edition 6 (платформа Java, стандартная редакция). Комплекту разработчика на Java было дано название JDK 6. По аналогии с J2SE 5 цифра 6 в Java SE 6 — это номер версии продукта. Внутренним номером версии разработчиков являлся 1.6. Выпуск Java SE 6 построен на основе J2SE 5 с добавлением поэтапных усовершенствований. Он не дополнял какими-либо важными средствами сам язык Java, но расширил библиотеки API, добавил ряд новых пакетов и предоставил усовершенствования исполняющей среды. Кроме того, в течение своего длинного (в понятиях Java) жизненного цикла он прошел через несколько модернизаций, попутно добавив некоторое количество обновлений. В целом выпуск Java SE 6 послужил дальнейшему укреплению достижений выпуска J2SE 5. Очередным выпуском Java стал Java SE 7 с названием комплекта разработчика JDK 7 и внутренним номером версии 1.7. Выпуск Java SE 7 был первым крупным выпуском Java после того, как компанию Sun Microsystems приобрела компания Oracle. Выпуск Java SE 7 содержал много новых функциональных средств, включая существенные дополнения языка и библиотек API. Новые языковые средства были разработаны в рамках проекта Project Coin. Целью проекта Project Coin была идентификация ряда небольших изменений языка Java, подлежащих включению в JDK 7. Ниже приведен список языковых средств, добавленных комплектом JDK 7. zzНаделение типа String способностью управлять оператором switch. zzДвоичные целочисленные литералы. zzСимволы подчеркивания в числовых литералах. zzРасширение оператора try под названием оператор try с ресурсами, который поддерживает автоматическое управление ресурсами. zzВыведение типов (через ромбовидную операцию) при конструировании экземпляра обобщенного типа. zzУсовершенствованная обработка исключений, при которой два или большее количество исключений можно перехватывать одним оператором catch (многократный перехват), и улучшенная проверка типов для исключений, генерируемых повторно.

Предисловие

21

Как видите, несмотря на то, что средства Project Coin считались небольшими изменениями, внесенными в язык, их преимущества были гораздо значительнее, чем можно было бы предположить по характеристике “небольшие”. В частности, оператор try с ресурсами основательно повлиял на способ написания значительного объема кода. Следующим выпуском Java был Java SE 8 с комплектом разработчика JDK 8. Он имел внутренний номер версии 1.8. Комплект JDK 8 стал значительным обновлением языка Java из-за включения многообещающего нового языкового средства: лямбда-выражений. Влияние лямбда-выражений было и продолжило быть весьма существенным, изменяя как способ концептуализации программных решений, так и способ написания кода Java. Помимо прочего лямбда-выражения содействуют упрощению и сокращению объема исходного кода, необходимого для создания определенных конструкций, таких как некоторые виды анонимных классов. Добавление лямбда-выражений также привело к появлению в языке новой операции (–>) и нового элемента синтаксиса. Кроме лямбда-выражений в JDK 8 было добавлено много других важных новых средств. Например, начиная с JDK 8, появилась возможность определять стандартную реализацию для метода, объявленного в интерфейсе. В конечном итоге Java SE 8 стал крупным выпуском, значительно расширившим возможности языка и изменившим способ написания кода Java. Следующим выпуском Java стал Java SE 9. Комплект разработчика назывался JDK 9, а внутренним номером версии также был 9. Комплект JDK 9 представлял крупный выпуск Java, включая значительные улучшения как языка Java, так и его библиотек. Главным новым средством JDK 9 были модули, которые позволили указывать взаимосвязи и зависимости кода, составляющего приложение. Модули также добавляют еще одно измерение к средствам контроля доступа Java. Включение модулей привело к добавлению в Java нового элемента синтаксиса, нескольких ключевых слов и различных усовершенствований инструментов. Модули оказали основательное влияние и на библиотеку API, т.к. начиная с JDK 9, библиотечные пакеты организованы в виде модулей. Помимо модулей в JDK 9 включен ряд других новых средств. Особый интерес представляет JShell — инструмент, который поддерживает интерактивное экспериментирование с программами и изучение Java. (Введение в JShell приведено в приложении Б.) Еще одним интересным обновлением является поддержка закрытых методов интерфейсов. Их добавление в дальнейшем расширяет поддержку JDK 8 стандартных методов в интерфейсах. В JDK 9 инструмент javadoc был снабжен возможностью поиска, для поддержки которой предусмотрен новый дескриптор под названием @index. Как и предшествующие выпуски, JDK 9 вносит несколько усовершенствований в библиотеки Java API. Обычно в любом выпуске Java наибольший интерес вызывают новые средства. Тем не менее, выпуск JDK 9 примечателен тем, что в нем объявлен устаревшим один заметный аспект Java: аплеты. Начиная с JDK 9, применять аплеты в новых проектах больше не рекомендуется. Как объяснялось ранее в главе,

22

Java: руководство для начинающих, 9-е издание

из-за ослабления поддержки аплетов со стороны браузеров (и других факторов) в выпуске JDK 9 объявлен устаревшим весь API для аплетов. Следующим выпуском Java был Java SE 10 (JDK 10). Тем не менее, до этого выпуска в графике выпусков Java произошло значительное изменение. В прошлом между крупными выпусками часто проходило два или более лет. Однако, начиная с JDK 10, время между выпусками значительно сократилось. Ожидается, что в соответствие со строгим графиком выхода расчетное время между крупными выпусками (которые теперь называются выпусками функциональных средств) составляет всего лишь полгода. В результате выпуск JDK 10 появился в марте 2018 года, т.е. спустя полгода после выпуска JDK 9. Такая увеличенная частота выпусков позволит программистам на Java получать своевременный доступ к новым средствам и улучшениям. Вместо того чтобы ждать два года или дольше, когда новое функциональное средство будет готово, оно становится частью следующего запланированного выпуска. Еще одним аспектом изменений в графике выпусков Java является выпуск с долгосрочной поддержкой (long-term support — LTS). Ожидается, что выпуск LTS будет производиться каждые три года. Выпуск LTS будет поддерживаться (и, следовательно, оставаться жизнеспособным) в течение периода времени, превышающего полгода. Первым выпуском LTS был JDK 11. Вторым выпуском LTS стал JDK 17, с учетом которого была обновлена эта книга. Из-за стабильности, предлагаемой выпуском LTS, вполне вероятно, что его набор средств будет определять базовый уровень функциональности для промежутка в несколько лет. Последние сведения о долгосрочной поддержке и графике выхода выпусков LTS ищите на веб-сайте Oracle. Главным новым языковым средством, добавленным JDK 10, стала поддержка выведения типов локальных переменных, благодаря которому теперь можно позволить выводить тип локальной переменной из типа ее инициализатора, а не указывать его явно. Для обеспечения такой возможности в Java было добавлено контекстно-чувствительное ключевое слово var. Выведение типов способно упростить код за счет устранения необходимости избыточно указывать тип переменной, когда его можно вывести из инициализатора переменной. Выведение типов также может упростить объявления в ситуациях, при которых тип трудно понять или невозможно указать явно. Выведение типов локальных переменных стало обычной частью современной программной среды. Его включение в Java помогло сохранять Java в актуальном состоянии с учетом меняющихся тенденций в проектировании языков. Наряду с другими изменениями в JDK 10 переопределена строка версии Java с изменением смысла номеров версий, чтобы они лучше соотносились с новым графиком выпуска. Следующим выпуском Java был Java SE 11 (JDK 11). Он вышел в сентябре 2018 года, т.е. через полгода после JDK 10, и был выпуском LTS. Главным новым языковым средством в JDK 11 являлась поддержка использования ключевого слова var в лямбда-выражениях. Кроме того, загрузчик приложений Java получил еще один режим выполнения, позволяющий напрямую запускать простые однофайловые программы. В JDK 11 также были удалены некоторые средства.

Предисловие

23

Возможно, наибольший интерес представляло удаление поддержки аплетов ввиду их исторической значимости. Вспомните, что аплеты впервые были объявлены нерекомендуемыми в JDK 9. В выпуске JDK 11 поддержка аплетов была удалена. Поддержка другой технологии развертывания, называемой Java Web Start, тоже была удалена в JDK 11. Еще одним ключевым изменением в JDK 11 стало то, что JavaFX больше не входит в состав JDK. Взамен эта инфраструктура для построения графических пользовательских интерфейсов превратилась в проект с открытым кодом. Из-за того, что упомянутые средства перестали быть частью JDK, в книге они не обсуждаются. Между LTS-выпуском JDK 11 и следующим LTS-выпуском (JDK 17) вышли пять выпусков функциональных средств: с JDK 12 до JDK 16. В выпусках JDK 12 и JDK 13 новые языковые средства не добавлялись. В выпуске JDK 14 была добавлена поддержка выражения switch, которое представляет собой конструкцию switch, производящую значение. Также были проведены другие усовершенствования switch. В выпуске JDK 15 появились текстовые блоки, по существу являющиеся строковыми литералами, которые могут занимать более одной строчки. В выпуске JDK 16 операция instanceof была расширена сопоставлением с образцом и добавлен новый тип класса под названием запись вместе с новым контекстно-чувствительным ключевым словом record. Запись предлагает удобный способ объединения данных. В выпуске JDK 16 также предоставляется новый инструмент пакетирования приложений, называемый jpackage. На момент написания книги самой последней версией Java была Java SE 17 (JDK 17). Как упоминалось ранее, это второй LTS-выпуск Java, что имеет особое значение. Его главное новое средство — возможность запечатывать классы и интерфейсы. Запечатывание дает вам контроль над наследованием класса, а также над наследованием и реализацией интерфейса. Для такой цели были добавлены контекстно-чувствительные ключевые слова sealed, permits и non-sealed (первое ключевое слово Java с дефисом). Кроме того, в JDK 17 произошла пометка API для аплетов как устаревшего и подлежащего удалению. Вы уже знаете, что поддержку аплетов убрали несколько лет назад. Однако API для аплетов просто был помечен как нерекомендуемый, что позволяло компилировать практически исчезающий код, который полагался на этот API. После выхода JDK 17 теперь API для аплетов подлежит удалению в будущем выпуске. И еще один момент относительно эволюции Java: в 2006 году начался процесс открытия исходного кода Java. В наши дни доступны реализации JDK с открытым кодом. Открытие исходного кода дополнительно способствует динамичной природе процесса разработки Java. В конечном итоге наследием инноваций Java является безопасность. Java остается живым и гибким языком, который привыкли ожидать в мире программирования. Материал настоящей книги обновлен с учетом JDK 17. Тем не менее, как подчеркивалось в предыдущем обсуждении, история программирования на Java характеризуется энергичными изменениями. Рекомендуется отслеживать новые возможности в каждом последующем выпуске Java. Проще говоря: эволюция Java продолжается!

24

Java: руководство для начинающих, 9-е издание

Структура книги Эта книга представляет собой учебное пособие, каждый раздел которого опирается на материал предыдущего раздела. Книга содержит 17 глав, в каждой из которых обсуждается тот или иной аспект языка Java. Уникальность книги в том, что она включает специальные элементы, которые закрепляют полученные знания.

Ключевые навыки и концепции Каждая глава начинается с перечня важнейших навыков, которые вы получите в результате изучения главы.

Вопросы и упражнения для самопроверки Каждая глава завершается вопросами и упражнениями, которые позволят вам проверить свои знания. Ответы находятся в приложении А.

Спросим у эксперта В книге повсеместно встречаются специальные врезки “Спросим у эксперта”, содержащие дополнительную информацию либо интересные комментарии по теме. В них используется формат “вопрос-ответ”.

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

Предыдущий опыт программирования не требуется В книге предполагается отсутствие у читателя предыдущего опыта программирования. Таким образом, если вы никогда раньше не программировали, то все равно можете воспользоваться этой книгой. При наличии опыта программирования вы сможете продвигаться немного быстрее. Однако имейте в виду, что Java обладает рядом ключевых отличий от других популярных языков программирования. Важно не делать поспешных выводов. Таким образом, даже для опытного программиста рекомендуется читать внимательно.

Необходимое программное обеспечение Для компиляции и запуска всех программ, рассмотренных в книге, вам понадобится последняя версия JDK, которой на момент написания книги является JDK 17. Это JDK для Java SE 17. Инструкции по получению Java JDK приведены в главе 1.

Предисловие

25

Если вы имеете дело с более ранней версией Java, то по-прежнему сможете пользоваться книгой, но утратите возможность компиляции и запуска программ, в которых задействованы новые функциональные средства Java.

Исходный код примеров Исходный код для всех примеров и проектов, рассмотренных в книге, доступен на веб-сайте издательства.

Благодарности Я хочу выразить особую благодарность Дэнни Коварду, научному редактору этого издания книги. Дэнни работал над несколькими моими книгами, и его советы, идеи и предложения всегда имели большую ценность и получали высокую оценку.

Ждем ваших отзывов! Вы, читатель этой книги, и есть главный ее критик. Мы ценим ваше мнение и хотим знать, что было сделано нами правильно, что можно было сделать лучше и что еще вы хотели бы увидеть изданным нами. Нам интересны любые ваши замечания в наш адрес. Мы ждем ваших комментариев и надеемся на них. Вы можете прислать нам электронное письмо либо просто посетить наш веб-сайт и оставить свои замечания там. Одним словом, любым удобным для вас способом дайте нам знать, нравится ли вам эта книга, а также выскажите свое мнение о том, как сделать наши книги более интересными для вас. Отправляя письмо или сообщение, не забудьте указать название книги и ее авторов, а также свой обратный адрес. Мы внимательно ознакомимся с вашим мнением и обязательно учтем его при отборе и подготовке к изданию новых книг. Наши электронные адреса: E-mail: WWW:

[email protected] http://www.dialektika.com

Глава 1 Основы языка Java

В этой

главе ...

История и философия языка Java Вклад Java в развитие Интернета Важность байт-кода Терминология Java Фундаментальные принципы объектно-ориентированного программирования Создание, компиляция и запуск простой программы на Java Использование переменных Применение управляющих операторов if и for Создание блоков кода Расположение, отступ и завершение операторов Ключевые слова Java Правила для идентификаторов Java

я

зык Java оказал влияние на ряд технологий в вычислительной технике. Его создание на заре существования Интернета помогло установить современ­ ную форму Интернета, включая клиентскую и серверную части. Инновацион­ ные средства Java продвинули вперед искусство и науку программирования , установив новый стандарт в разработке языков программирования. Культура дальновидного мышления, которая образовалась вокруг языка Java, гарантиро­ вала, что он останется ярким и живым, адаптируясь к зачастую быстрым и раз­ нообразным изменениям во вселенной вычислений. В целом Java не только один из самых важных языков программирования в мире, но сила, которая про­ извела революцию в программировании и в ее ходе изменила мир. Хотя язык Java часто ассоциируется с программированием приложений для Интернета, он никоим образом не ограничен в данном отношении . Java мощный, полнофункциональный язык программирования общего назначения. Таким образом, если вы новичок в программировании, то Java будет превосход­ ным вариантом для изучения. Более того, чтобы считаться профессиональным программистом в наши дни, важно уметь программировать на Java. В ходе чте­ ния книги вы получите основные навыки, которые помогут овладеть языком Java. -

-

Глава .1 . . О сн_ овы_язык� .J а v а Целью настоящей главы является ознакомление с языком

Java,

.

. .

�9

начиная с его

истории, проектной философии и ряда наиболее важных характеристик. Безус­ ловно , самое сложное в изучении языка программирования - тот факт, что ни один элемент не существует в изоляции . Наоборот, компоненты языка работают в сочетании друг с друго м . Взаимосвязь между ними особенно ярко выражена в

Java.

Н а самом деле довольно трудно обсуждать один аспект языка

Java,

не

затрагивая другие. Для преодоления такой проблемы в этой главе представлен краткий обзор некоторых особенностей граммы на

Java,

Java,

в том числе общей формы про­

нескольких основных управляющих структур и простых опера­

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

Java.

И ст ория и фи л ософия языка Java Прежде чем удастся в полной мере оценить уникальные аспекты языка

Java,

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

Java

были прямым или косвенным

результатом действия исторических сил , сформировавших язык. Таким обра­ зом, вполне уместно, что изучение

Java

начинается с выяснения того , как

Java

соотносится с более широкой вселенной программирования .

П роисхо ждение Java Язык

Java

придумали в

1 99 1

году Дже ймс Гослинг, Патрик Ното н , Крис

Барт, Эд Франк и Майк Шеридан, которые работали в чала язык назывался

Oak,

но в

1 995

Sun

Microsystems. Сна­

году его переименовали в

ление первоначальной движущей силой появления

Java

Java.

На удив­

был вовсе не И нтер­

нет! Основной мотивацией оказалась потребность в наличии независимого от платформы языка , который позволил бы создавать программное обеспечение, встраиваемое в различные бытовые электронные устройства, такие как тосте­ ры, микроволновые печи и пульты дистанционного управления. Наверняка вы догадались, что в качестве контроллеров использовались центральные процес­ соры ( ЦП) самых разных типов. Проблема заключалась в том , что (в то время) большинство языков программирования предусматривали компиляцию в ма­ шинный код, предназначенный для определенного типа ЦП. Рассмотрим , на­ пример , язык С++. Хотя программу на С++ можно было скомпилировать практически для лю­ бого типа ЦП, требовался полный компилятор С++, предназначенный для кон­ кретного ЦП. Однако проблема заключалась в том , что создание компиляторов сопряжено с финансовыми затратами и временем. Пытаясь отыскать лучшее решение, Гослинг и другие трудились над переносимым межплатформенным языком , который позволил бы производить код, способный функционировать на различных ЦП в разных средах. Результатом всех их усилий стал язык

Java.

3.()

. )(]VO: ру�ОВ()�СТВ() ДJlЯ_ 1-юч_и н_О ЮLЦ_И Х , ?�.� ИЗ�=

Меньше

я з ы ке Java

Меньше или равно Больше Больше или равно Р авно

!=

Не равно

Обратите внимание , что проверка на равенство обозначается двумя знаками равенства.

. . Гл_а в(] 1 _()с н_ов ы _язык(] J av(] . . ..

. .

. .

Ниже приведена программа, в которой иллюстрируется работа оператора

-�!) . if:

/* Демо нс трация исполь зования оператора i f . Назо вите э т о т файл I f Demo . j ava . */ c l a s s I f Demo { puЫ i c s t a t i c void ma i n ( S t r i n g [ ] args ) int а , Ь , с ;

{

а = 2; ь = 3; i f ( a < Ь ) S y s t em . ou t . p r i n t l n ( " Знaчeниe а меньше значе ния Ь " ) ; 1 1 Следуюший оператор ничего не отобразит . i f ( a == Ь ) S y s t em . out . pr i n t l n ( " Э тo вы не увидите " ) ; S y s t em . out . p r i n t l n ( ) ; с = а

-

Ь;

/ / Переменная с содержит - 1

S y s t em . out . p r i n t l n ( " Пepeмe ннaя с содержит - 1 " ) ; i f ( c >= 0 ) S y s t em . out . p r i n t l n ( " Знaчe ниe с неотрицательное " ) ; i f ( c < 0 ) S y s t em . out . p r i n t l n ( " Знaчe ниe с о трицательное " ) ; S y s t em . out . p r i n t l n ( ) ; с = Ь

-

а;

/ / Теперь переменная с с одержит 1

Sys tem . out . p r i n t l n ( " Пepeмeннaя с содержит 1 " ) ; i f ( c >= 0 ) S y s t em . out . p r i n t l n ( " Знaчe ниe с нео трицательное " ) ; i f ( c < 0 ) S y s t em . out . p r i n t l n ( " Знaчe ниe с отрицательное " ) ;

Вот вывод, генерируемый программой :

Значе ние а меньше значе ния Ь Переменная с содержит - 1 Значение с о трицательное Переменная с содержит 1 Значение с нео трицательное С программой связан еще один момент. В следующей строке посредством списка с разделителем-запятой объявляются три переменные

-

а,

Ь

и с:

int а, Ь, с ; Как упоминалось ранее , когда требуются две или более переменных одного типа, их можно объявить в одном операторе, просто разделяя имена перемен­ ных запятыми.

Ц и кл for За счет создания

цикла

можно многократно выполнять кодовую последо­

вательность. Циклы применяются всякий раз, когда нужно выполнить повто ­ ряющуюся задачу, потому что реализовать их гораздо легче , чем многократно

записывать одну и ту же последовательность операторов. Язык

Java поддержи­

вает мощный набор конструкций циклов. Здесь мы рассмотрим цикл

for,

кото­

рый имеет следующий вид: f о r ( инициализация; условие ; итерация)

опера тор ;

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

ную управления циклом. Если результат проверки оказывается истинным , тогда опера тор выполняется, а цикл

for

продолжает работу. При ложном результате

проверки цикл завершается. Выражение итера ция определяет, каким образом переменная управления циклом изменяется на каждой итерации цикла. Далее приведена короткая программа, иллюстрирующая работу цикла

for:

/* Демон с трация испол ь з о вания цикла f o r . Назовите э т о т файл For Demo . j ava . */ c l a s s ForDemo { puЫ i c s t a t i c void main ( S t r i n g [ ] a rg s ) i n t count ;

{

f o r ( c ount = О ; count < 5 ; count = coun t + l ) - Этот цикл S y s t em . ou t . p r i n t l n ( " Знaчeниe count : " + coun t ) ;

выполняется пять раз

S y s t em . o u t . p r i n t l n ( " Гo т o в o ! " ) ;

Вот вывод, генерируемый программой:

Значе ние Значение Значе ние Значе ние Значение Готово !

count : coun t : count : count : count :

о 1 2 з 4

В приведенном примере

count

является переменной управления циклом.

Она инициализируется нулем в части инициализа ция цикла

for.

В начале каж­

дой итерации (включая первую) выполняется условная проверка

count < 5 .

Если результат проверки оказывается истинным , тогда выполняется оператор

println ( ) , после чего выполняется часть итерация цикла, которая увеличивает значение count на 1 . Процесс продолжается до тех пор, пока условная провер­ ка не станет ложной. Интересно отметить, что в профессионально написанных программах на

Java вы практически никогда не встретите часть итерация цикла,

написанную так, как в предыдущей программе. То есть вы будете редко видеть операторы вроде следующего :

count

=

count + 1 ;

. . . Гл_ О В (J . .1 . .. () сн_ ов �1 _ я ;, ык(] ) av(J . . . _f,! Причина в том, что в Java есть специальная операция инкремента, обладаю­ щая большей эффективностью, которая обозначается посредством + + (т.е. два знака "плюс" подряд). Операция инкремента увеличивает свой операнд на еди­ ницу. С помощью операции инкремента предыдущее выражение можно запи­ сать в показанной ниже форме: coun t + + ;

Таким образом, цикл f o r в предыдущей программе обычно будет записы­ ваться в следующем виде: for ( count

=

О ; count < 5 ; coun t + + )

Можете опробовать его. Вы заметите, что цикл выполняется в точности, как бьшо ранее. В Java также предлагается операция декремента, обозначаемая как - - . Она уменьшает свой операнд на единицу.

Созда н ие бл оков кода Язык Java позволяет группировать два или более операторов в блоки кода, также называемые кодовыми блоками. Для этого операторы помещаются между открывающей и закрывающей фигурными скобками. После того, как блок кода создан , он становится логической единицей, которую можно применять в лю­ бом месте, где разрешено использовать одиночный оператор. Скажем, блок мо­ жет служить телом для операторов if и for. Возьмем следующий оператор i f : if (w < h) v = w * h; w = О;

----- Начало блока +----

Конец блока

Если w меньше h, тогда выполнятся оба оператора внутри блока. Таким об­ разом, два оператора внутри блока образуют логическую единицу, где первый оператор не может быть выполнен без выполнения второго. Ключевой момент здесь в том, что всякий раз , когда нужно логически связать два или большее количество операторов, вы создаете блок. Блоки кода позволяют реализовывать многие алгоритмы более ясно и эффективно. В приведенной ниже программе блок кода применяется для предотвращения деления на ноль: /* Демонстрация исполь зования блока кода . Назовите э т о т файл B l o c kDemo . j ava . */ c l a s s B l o c kDemo { puЫ i c s t a t i c void ma i n ( S t r i n g [ ] args ) { douЫe i , j , d ; i = 5; j = 10;

/ / Целью оператора if я вляе т с я бло к . if ( i ! = 0) { S y s t em . ou t . p r i п t l п ( " Знaчeниe i не равно нулю . " ) ; d = j / i '· . . . S y s t em . ou t . p r i п t l п ( " Peзyл ь т a т J / 1 равен " + d ) ;

]- телом оператора является целы й блок

if

Вот вывод, генерируемый программой: Значе ние i не равно нулю . Резул ь т а т j / i равен 2 . 0

В данном случае целью оператора i f является блок кода, а не одиночный оператор. Если условие if дает t rue (как здесь) , то будут выполнены три опе­ ратора внутри блока. Попробуйте установить i в ноль и взгляните на результат. Вы обнаружите , что целый блок будет пропущен.

С П РОСИМ

У

ЭКСП ЕРТА

ВОП РОС. Приводит ли использование блока кода к снижению эффективно­

сти программы во время выполнения? Другими словами, действительно ли предпринимаются какие-то добавочные действия, когда встречаются симво­ лы { и ) ? ОТВЕТ. Нет. Никакие накладные расходы с блоками кода не связаны. На самом

деле благодаря тому, что они упрощают написание кода определенных ал­ горитмов, их применение обычно увеличивает скорость и эффективность. Кроме того, символы { и ) существуют только в исходном коде программы и с ними не связаны какие-либо дополнительные действия.

Позже в книге вы увидите, что блоки кода обладают дополнительными ха­ рактеристиками и сценариями использования. Однако главная причина их су­ ществования - создание логически неразрывных единиц кода.

Точки с за пято й и размещение операторов Точка с запятой в Java является разделителем и часто применяется для завер­ шения оператора. По сути, с помощью точки с запятой помечается конец одной логической сущности. Как вам уже известно, блок представляет собой набор логически связанных операторов, заключенных в пару открывающей и закрывающей фигурных ско­ бок. Блок не заканчивается точкой с запятой. Взамен конец блока обозначается закрывающей фигурной скобкой. Конец строки Java не считается окончанием оператора, а потому не имеет значения, где он находится в строке кода. Скажем, следующие строки:

. . . Глав(] 1 () снов �� .язык (] )ауа . . . !)9 . . ..

.

у = у + 1; S y s t em . out . p r i n t l n ( x + " " + у ) ;

эквивалентны одной строке: х = у ; у = у + 1 ; S y s t em . ou t . p r i n t l n ( x + " " + у ) ;

Кроме того, индивидуальные элементы оператора могут размещаться в раз­ ных строках. Например, совершенно допустим оператор следующего вида: S y s t em . out . p r i n t l n ( " Длиннaя строка вывода " + x + y + z + " продолже ние вывода " ) ;

Разбиение длинных строк подобным способом часто имеет целью повыше­ ние читабельности программы, а также помогает предотвратить автоматический перенос слишком длинных строк.

П ра кти ка отступов В ы наверняка заметили в предыдущих примерах, что некоторые операторы записаны с отступами. Java язык свободной формы, поэтому не имеет значе­ ния, каким образом операторы размещаются друг относительно друга в строке. Тем не менее, с годами бьm разработан общепринятый стиль отступов, который позволяет создавать удобные для восприятия программы. Такой стиль соблюда­ ется в книге и вам рекомендуется поступать аналогично. Стиль предусматривает отступ на один уровень после каждой открывающей скобки и возврат назад на один уровень после каждой закрывающей скобки. Позже будет показано, что некоторым операторам требуются дополнительные отступы. -

Уп ражнение 1 .2

Усове р ш енствованная ве р сия п р о г р а ммы для п р ео б р а з ования галлонов в ли тр ы

, . """ . " . """" . """" ... " . " . " .... " . " . " . . .. , Используя цикл for ' оператор i f и блоки кода ' можно j Ga l T oL i tTaЬle . j ava ! : . . " . " . .. " . ... . .. .. . " . " ......... " . " .. . . " ... " . " : создать усовершенствованную версию программы преобразования галлонов в литры, которая была разработана в упражнении 1 . 1 . Новая версия будет выводить таблицу преобразований, начиная с 1 и заканчи­ вая 100 галлонами. После каждых 10 галлонов будет выводиться пустая строка. Это достигается за счет применения переменной counter, подсчитывающей ко­ личество выведенных строк. Обратите особое внимание на ее использование. Создайте файл по имени GalToLitTaЬle . j ava. Поместите в GalToLitTaЬle . j ava следующий код программы: /* Упражнение 1 . 2 . Э т а про грамма о т о бражает т аблицу пре образова ний галлонов в литры . Назовите э т о т файл GalToLitTaЫe . j ava . */

c l a s s G a l T o L i tTaЬle { puЫ i c s t a t i c void rn a i п ( S t r i п g [ ] a r g s ) douЫe g a l l oп s , l i t e r s ; i п t couп t e r ;

{

Установить счетчик в ноль

couпter = О ; строк сночола f o r ( ga l l o п s = 1 ; g a l l o п s 9));

72

Java: руководство для начинающих, 9-е издание Программа генерирует следующий вывод: b равно false b равно true Данная строка кода выполняется. 10 > 9 равно true

В этой программе необходимо отметить три интересных момента. Вопервых, как видите, когда println() выводит значение boolean, отображается true или false. Во-вторых, самого по себе значения переменной boolean достаточно для управления оператором if, т.е. нет необходимости записывать оператор if в таком виде: if(b == true) ...

В-третьих, результатом операции отношения, подобной 9 дает значение true. Кроме того, дополнительный набор скобок вокруг 10>9 нужен из-за того, что приоритет операции + выше приоритета операции >. Упражнение 2.1

Далеко ли до места вспышки молнии?

В данном проекте создается программа, которая вычисляет расстояние (в метрах) между наблюдателем и местом вспышки молнии. Звук распространяется по воздуху со скоростью примерно 335 метров в секунду. Таким образом, зная интервал между моментом, когда появилась вспышка молнии, и моментом, когда был услышан звук, можно рассчитать расстояние до места вспышки. Предположим, что временной интервал составляет 7,2 секунды. Sound.java

1. Создайте файл по имени Sound.java.

2. Имейте в виду, что при расчете расстояния должны использоваться значения с плавающей точкой. Почему? Да потому что временной интервал 7,2 имеет дробную часть. Хотя вполне подошел бы тип float, в примере будет применяться тип double.

3. Для вычисления расстояния умножьте 7,2 на 335 и присвойте результат переменной. 4. В заключение отобразите результат. Ниже показан полный код программы Sound.java: /*

Упражнение 2.1. Расчет расстояния до места вспышки молнии, звук которого был услышан через 7.2 секунды.

*/ class Sound { public static void main(String[] args) {

Глава 2. Введение в типы данных и операции

73

double dist;

dist = 7.2 * 335;

System.out.println("Место вспышки молнии находится на расстоянии     " + dist + " метров."); }

}

5. Скомпилируйте и запустите программу. Отобразится следующий результат: Место вспышки молнии находится на расстоянии 2412.0 метров.

6. Дополнительная задача: рассчитать расстояние до крупного объекта вроде скалы можно по времени прихода эхо. Например, если вы хлопнете в ладоши и измерите время, через которое стало слышно эхо, то узнаете общее время прохождения звука туда и обратно. Разделив это значение на два, вы получите время, за которое звук проходит в одну сторону. Затем результирующее значение можно использовать для расчета расстояния до объекта. Модифицируйте предыдущую программу так, чтобы она вычисляла расстояние, исходя из предположения о том, что временной интервал представляет собой промежуток времени до прихода эхо.

Литералы В языке Java под литералами понимаются фиксированные значения, представленные в удобочитаемой форме. Скажем, число 100 является литералом. Литералы также обычно называют константами. По большей части литералы и их применение настолько интуитивно понятно, что они использовались в той или иной форме во всех предыдущих примерах программ. Теперь пришло время объяснить их формально. Литералы Java могут относиться к любому примитивному типу данных. Способ представления каждого литерала зависит от его типа. Как объяснялось ранее, символьные константы заключаются в одинарные кавычки, например, 'a' и '%'. Целочисленные литералы указываются как числа без дробных частей подобно 10 и -100. Литералы с плавающей точкой требуют применения десятичной точки, за которой следует дробная часть числа, скажем, 11.123. Для чисел с плавающей точкой также разрешено использовать экспоненциальную запись. Целочисленные литералы по умолчанию имеют тип int. Чтобы указать литерал типа long, понадобится добавить к константе букву l или L. Например, 12 – литерал типа int, а 12L – литерал типа long. По умолчанию литералы с плавающей точкой имеют тип double. Чтобы указать литерал типа float, необходимо добавить к константе букву F или f. Например, 10.19F – литерал типа float.

74

Java: руководство для начинающих, 9-е издание

Хотя целочисленные литералы по умолчанию создаются как значения типа int, их все же разрешено присваивать переменным типа char, byte или short, если присваиваемое значение может быть представлено целевым типом. Целочисленный литерал всегда можно присвоить переменной типа long. В целочисленный литерал или литерал с плавающей точкой можно включить один или несколько символов подчеркивания, что позволит облегчить чтение значений, состоящих из многих цифр. Когда литерал компилируется, символы подчеркивания просто отбрасываются. Вот пример: 123_45_1234

Данный литерал задает значение 123451234. Применение символов подчеркивания особенно полезно при указании номеров деталей, идентификаторов клиентов и кодов состояния, которые обычно состоят из подгрупп цифр.

Шестнадцатеричные, восьмеричные и двоичные литералы Как известно, в программировании иногда проще использовать систему счисления, основанную на 8 или 16, а не на 10. Система счисления, основанная на 8, называется восьмеричной, и в ней применяются цифры от 0 до 7. В восьмеричной системе счисления число 10 соответствует числу 8 в десятичной форме. Система счисления с основанием 16 называется шестнадцатеричной и использует цифры от 0 до 9 плюс буквы от A до F, которые обозначают 10, 11, 12, 13, 14 и 15. Например, шестнадцатеричное число 10 равно десятичному числу 16. Из-за частого применения этих двух систем счисления Java позволяет указывать целочисленные литералы в шестнадцатеричной или восьмеричной форме вместо десятичной. Шестнадцатеричный литерал должен начинаться с 0x или 0X (ноль, за которым следует буква x или X). Восьмеричный литерал начинается с нуля. Ниже приведены примеры: hex = 0xFF; oct = 011;

// соответствует десятичному числу 255 // соответствует десятичному числу 9

Интересно отметить, что в Java также разрешены шестнадцатеричные литералы с плавающей точкой, но они используются редко. Целочисленный литерал можно указывать с помощью двоичного кода, для чего перед двоичным числом следует поставить 0b или 0B. Скажем, 0b1100 задает значение 12 в двоичном формате.

Управляющие последовательности символов Заключение символьных констант в одинарные кавычки подходит для большинства печатаемых символов, но некоторые символы, такие как возврат каретки, создают особую проблему при работе в текстовом редакторе. Кроме того, ряд других символов вроде одинарных и двойных кавычек имеют специальное предназначение в Java и потому их нельзя применять напрямую. По указанным причинам в Java предусмотрены специальные управляющие последовательности,

Глава 2. Введение в типы данных и операции

75

иногда называемые символьными константами с обратной косой чертой, которые описаны в табл. 2.3. Такие последовательности используются вместо символов, которые они представляют.

Таблица 2.3. Управляющие последовательности символов Управляющая последовательность \' \" \\ \r \n \f \t \b \ddd \uxxxx \s \конец-строки

Описание Одинарная кавычка Двойная кавычка Обратная косая черта Возврат каретки Новая строка (также известная как перевод строки) Подача страницы Табуляция Забой Восьмеричный символ (ddd – восьмеричная константа) Шестнадцатеричный символ Unicode (xxxx – шестнадцатеричная константа) Пробел (последовательность добавлена в JDK 15) Строка продолжения (применяется только к текстовым блокам; последовательность добавлена в JDK 15)

Следующий оператор присваивает переменной ch символ табуляции: ch = '\t';

А так переменной ch можно присвоить символ одинарной кавычки: ch = '\'';

Строковые литералы В Java поддерживается еще один тип литерала: строка. Строка представляет собой набор символов, заключенных в двойные кавычки: "простой тест"

Примеры строк встречались во многих операторах println() в приведенных ранее программах. В дополнение к обычным символам строковый литерал может содержать одну или несколько только что описанных управляющих последовательностей. Рассмотрим следующую программу, в которой применяются управляющие последовательности \n и \t:

76

Java: руководство для начинающих, 9-е издание

// Демонстрация использования управляющих последовательностей в строках. class StrDemo { public static void main(String[] args) { System.out.println("Первая строка\nВторая строка"); System.out.println("A\tB\tC"); Использовать последовательность \n System.out.println("D\tE\tF"); для вставки символа новой строки } } Использовать символы табуляции для выравнивания вывода

Ниже показан вывод, генерируемый программой: Первая Вторая A D

строка строка B C E F

Спросим у эксперта ВОПРОС. Является ли строка, состоящая из одного символа, той же самой сущностью, что и символьный литерал? Например, совпадают ли "k" и 'k'? ОТВЕТ. Нет. Не путайте строки с символами. Символьный литерал представляет одиночную букву типа char. Строка, содержащая только одну букву, попрежнему считается строкой. Хотя строки состоят из символов, они относятся к разным типам.

Обратите внимание, что для вставки символа новой строки используется управляющая последовательность \n. Чтобы получить многострочный вывод, вовсе не обязательно применять несколько операторов println(), а нужно просто помещать \n в те места более длинной строки, где должны вставляться символы новой строки. И еще один момент: как будет показано в главе 5, недавно в Java появилось функциональное средство, которое называется текстовым блоком. Текстовый блок предлагает более высокую степень контроля и гибкости в ситуациях, когда требуется несколько строк текста.

Подробный анализ переменных Переменные были представлены в главе 1, а здесь они рассматриваются более подробно. Как вы узнали ранее, переменные объявляются с использованием оператора следующего вида: тип имя-переменной;

В тип указывается тип данных переменной, а в имя-переменной – ее имя. Объявлять можно переменную любого допустимого типа, включая только что описанные простые типы, и каждая переменная будет иметь тип. Таким образом, возможности переменной определяются ее типом. Например, переменная типа boolean не может применяться для хранения значений с плавающей

Глава 2. Введение в типы данных и операции

77

точкой. Кроме того, тип переменной не может быть изменен в течение времени ее жизни. Скажем, переменная int не может превратиться в переменную char. Все переменные в Java должны быть объявлены до их использования, поскольку компилятору необходимо знать, данные какого типа содержит переменная, прежде чем он сможет правильно скомпилировать любой оператор, в котором применяется переменная. Кроме того, появляется возможность выполнения строгой проверки типов.

Инициализация переменной Как правило, перед использованием переменной должно быть присвоено значение. Вы уже видели, что один из способов предусматривает применение оператора присваивания. Другой способ – присваивание переменной начального значения при ее объявлении. Для этого после имени переменной ставится знак равенства и нужное значение. Вот общая форма инициализации: тип переменная = значение;

В значение указывается значение, присваиваемое переменной переменная при ее создании. Значение должно быть совместимым с типом, указанным в тип. Ниже приведено несколько примеров: int count = 10; char ch = 'X'; float f = 1.2F;

// присвоить count начальное значение 10 // инициализировать ch буквой X // инициализировать f значением 1.2

При объявлении двух или более переменных одного типа, используя список с разделителями-запятыми, начальное значение можно присваивать одной или нескольким переменным: int a, b = 8, c = 19, d; // переменные b и c имеют инициализаторы

В данном случае инициализируются только переменные b и c.

Динамическая инициализация Несмотря на то что в предшествующих примерах в качестве инициализаторов применялись только константы, Java позволяет инициализировать переменные динамически с использованием любого выражения, действительного на момент объявления переменной. Скажем, вот короткая программа, которая вычисляет объем цилиндра по радиусу его основания и высоте: // Демонстрация динамической инициализации. class DynInit { public static void main(String[] args) { double radius = 4, height = 5;

// Динамически инициализировать volume. double volume = 3.1416 * radius * radius * height;

}

}

System.out.println("Объем составляет " + volume);

Переменная volume динамически инициализируется во время выполнения

78

Java: руководство для начинающих, 9-е издание

Здесь объявляются три локальные переменные: radius, height и volume. Первые две, radius и height, инициализируются константами, а volume динамически инициализируется значением объема цилиндра. Ключевой момент в том, что в выражении инициализации может присутствовать любой элемент, допустимый во время инициализации, в том числе вызовы методов, другие переменные или литералы.

Область видимости и время жизни переменных До сих пор все применяемые переменные были объявлены в начале метода main(). Тем не менее, в Java разрешено объявлять переменные в любом блоке. Как объяснялось в главе 1, блок начинается с открывающей фигурной скобки и заканчивается закрывающей фигурной скобкой. Блок определяет область видимости. Таким образом, каждый раз, когда начинается новый блок, создается новая область видимости. Область видимости устанавливает, какие объекты доступны другим частям вашей программы. Она также определяет время жизни этих объектов. В общем случае каждое объявление в Java имеет область видимости. В результате Java определяет мощную и детализированную концепцию области видимости. Две наиболее распространенных области видимости в Java определяются классом и методом. Обсуждение области видимости класса (и объявленных в нем переменных) откладывается до рассмотрения классов, а сейчас речь пойдет только об областях видимости, определенных методом или внутри него. Область видимости, определяемая методом, начинается с его открывающей фигурной скобки. Однако если у метода есть параметры, то они тоже входят в область видимости метода. Область видимости метода заканчивается закрывающей фигурной скобкой. Такой блок кода называется телом метода. Как правило, переменные, объявленные внутри области видимости, не будут доступны в коде за рамками этой области. Таким образом, когда вы объявляете переменную в области видимости, то локализуете ее и защищаете от несанкционированного доступа и/или модификации. Действительно, правила области видимости обеспечивают основу для инкапсуляции. Переменная, объявленная внутри блока, называется локальной переменной. Области видимости могут быть вложенными. Например, создавая блок кода, вы создаете новую вложенную область. В таком случае внешняя область видимости охватывает внутреннюю область видимости. В итоге объекты, объявленные во внешней области, будут доступны коду во внутренней области. Тем не менее, обратное утверждение неверно. Объекты, объявленные во внутренней области, не доступны за ее пределами. Чтобы лучше понять вложенные области видимости, взгляните на следующую программу:

Глава 2. Введение в типы данных и операции

79

// Демонстрация области видимости блока. class ScopeDemo { public static void main(String[] args) { int x; // переменная известна всему коду внутри main() x = 10; if(x == 10) { // начало новой области видимости int y = 20;

// переменная известна только этому блоку

// Переменные x и y здесь известны.

System.out.println("x и y: " + x + " " + y); x = y * 2;

} // y = 100; // Ошибка! Переменная y здесь неизвестна

}

}

Здесь переменная y находится за пределами своей области видимости

// Переменная x здесь по-прежнему известна. System.out.println("Значение x равно " + x);

Как указано в комментариях, переменная x объявляется в начале области видимости метода main() и доступна во всем последующем коде main(). Внутри блока if объявляется переменная y. Поскольку блок определяет область видимости, переменная y видна только остальному коду внутри данного блока. Вот почему вне своего блока строка y  =   100; закомментирована. Если удалить символы комментария, тогда возникнет ошибка на этапе компиляции, т.к. переменная y не доступна за пределами своего блока. Внутри блока if можно работать с x, потому что код внутри блока (т.е. во вложенной области видимости) имеет доступ к переменным, объявленным в охватывающей области. Переменные могут быть объявлены в любом месте блока, но они будут действительными только после объявления. Таким образом, если переменная определена в начале метода, то она доступна во всем коде внутри этого метода. И наоборот, переменная, объявленная в конце блока, по существу бесполезна, т.к. никакой код не будет иметь к ней доступа. Есть еще один важный момент, о котором следует помнить: переменные создаются при входе в свою область видимости и уничтожаются при выходе из этой области видимости. Другими словами, переменная не будет хранить свое значение после того, как покинет свою область видимости, а потому переменные, объявленные внутри метода, не сохраняют значения между его вызовами. Кроме того, переменная, объявленная внутри блока, утрачивает свое значение при выходе из блока. Таким образом, время жизни переменной ограничено ее областью видимости. Если объявление переменной включает инициализатор, тогда переменная будет повторно инициализироваться каждый раз при входе в блок, в котором она объявлена. Например, рассмотрим показанную ниже программу: // Демонстрация времени жизни переменной. class VarInitDemo {

80

Java: руководство для начинающих, 9-е издание public static void main(String[] args) { int x;

}

}

for(x = 0; x < 3; x++) { int y = -1; // y инициализируется при каждом входе в блок System.out.println("Значение y равно: " + y); // всегда выводится -1 y = 100; System.out.println("Теперь значение y равно: " + y); }

Вот вывод, генерируемый программой: Значение y равно: Теперь значение y Значение y равно: Теперь значение y Значение y равно: Теперь значение y

-1 равно: 100 -1 равно: 100 -1 равно: 100

Несложно заметить, что переменная y повторно инициализируется значением -1 при каждом входе во внутренний цикл for. Несмотря на последующее присваивание y значения 100, это значение утрачивается. С правилами областей видимости Java связана одна удивительная особенность: хотя блоки могут быть вложенными, никакая переменная, объявленная во внутренней области видимости, не может иметь такое же имя, как у переменной, объявленной во внешней области видимости. Например, следующая программа, в которой предпринимается попытка объявить две отдельные переменные с одинаковыми именами, не скомпилируется. /* В этой программе предпринимается попытка объявить во внутренней области переменную с таким же именем, как у переменной, определенной во внешней области. *** Программа не скомпилируется. *** */ class NestVar { public static void main(String[] args) { int count;

for(count = 0; count < 10; count = count+1) { System.out.println("Значение count: " + count);

Нельзя объявлять переменную

}

}

}

count, поскольку она уже int count; // Не разрешено!!! определена for(count = 0; count < 2; count++) System.out.println("Программа содержит ошибку!");

Глава 2. Введение в типы данных и операции

81

Операции Язык Java обеспечивает развитую среду операций. Операция – это символ, который сообщает компилятору о необходимости выполнить определенное математическое или логическое действие. В Java есть четыре основных класса операций: арифметические операции, побитовые операции, операции отношения и логические операции. Кроме того, также определены дополнительные операции, предназначенные для обработки ряда особых ситуаций. В настоящей главе рассматриваются арифметические операции, операции отношения и логические операции, а также операция присваивания. Побитовые и другие специальные операции будут обсуждаться позже.

Арифметические операции В табл. 2.4 перечислены арифметические операции, которые определены в Java.

Таблица 2.4. Арифметические операции Java Операция

Описание

+ * / % ++ --

Сложение (также унарный плюс) Вычитание (также унарный минус) Умножение Деление Остаток от деления Инкремент Декремент

Операции +, -, * и / в Java работают точно так же, как в любом другом языке программирования (впрочем, как и в алгебре). Их можно применять к любому встроенному числовому типу данных, а также использовать для объектов типа char. Хотя действия арифметических операций хорошо известны всем читателям, некоторые особые ситуации требуют пояснений. Прежде всего, не забывайте, что когда операция / применяется к целому числу, остаток от деления будет отброшен; скажем, при целочисленном делении 10/3 дает 3. Получить остаток от целочисленного деления можно с помощью операции %. Например, результатом 10  %  3 будет 1. Операцию % в Java можно применять как к целочисленным типам, так и к типам с плавающей точкой. Таким образом, результат 10.0   %   3.0 также равен 1. Использование операции получения остатка от деления демонстрируется в следующей программе.

82

Java: руководство для начинающих, 9-е издание // Демонстрация использования операции %. class ModDemo { public static void main(String[] args) { int iresult, irem; double dresult, drem; iresult = 10 / 3; irem = 10 % 3; dresult = 10.0 / 3.0; drem = 10.0 % 3.0; System.out.println("Результат и остаток от деления 10 / 3: " + iresult + " " + irem); System.out.println("Результат и остаток от деления 10.0 / 3.0: " + dresult + " " + drem); } }

Ниже показан вывод, генерируемый программой: Результат и остаток от деления 10 / 3: 3 1 Результат и остаток от деления 10.0 / 3.0: 3.3333333333333335 1.0

Как видите, результатом операции % является остаток от целочисленного деления и деления с плавающей точкой, равный 1.

Инкремент и декремент Описанные в главе 1 операции ++ и -- являются операциями инкремента и декремента. Как вы увидите, они обладают рядом характеристик, которые делают их довольно интересными. Начнем с рассмотрения того, что делают операции инкремента и декремента. Операция инкремента добавляет 1 к своему операнду, а операция декремента вычитает 1 из своего операнда. Таким образом, оператор x = x + 1;

эквивалентен x++;

а оператор x = x - 1;

эквивалентен x--;

Операции инкремента и декремента могут либо предшествовать операнду (префиксная форма), либо следовать за операндом (постфиксная форма). Скажем, оператор x = x + 1;

можно записать так: ++x;

// префиксная форма

или так x++;

// постфиксная форма

Глава 2. Введение в типы данных и операции

83

В приведенном выше примере нет разницы, какая форма применяется – префиксная или постфиксная. Однако когда операция инкремента или декремента является частью более крупного выражения, то имеется важное отличие. Когда операция инкремента или декремента предшествует своему операнду, то сначала выполняется операция, а затем значение операнда используется в остальной части выражения. Если операция инкремента или декремента находится после своего операнда, то в выражении будет применяться значение операнда до инкрементирования или декрементирования. Рассмотрим следующие операторы: x = 10; y = ++x;

В данном случае переменная y получит значение 11. Тем не менее, если код будет иметь показанный ниже вид: x = 10; y = x++;

то значением переменной y окажется 10. В обоих случаях переменная x устанавливается в 11; разница лишь в том, когда это происходит. Возможность контроля над тем, когда происходит операция инкремента или декремента, обеспечивает значительные преимущества.

Операции отношения и логические операции Операции отношения и логические операции отличаются тем, что первые касаются отношений, которые значения имеют друг с другом, а вторые – способов связи истинных и ложных значений. Поскольку результатами операций отношения являются истинные или ложные значения, они часто используются с логическими операциями, и потому здесь обсуждаются вместе. Операции отношения перечислены в табл. 2.5.

Таблица 2.5. Операции отношения Java Операция

Описание

== != > < >= false в Java не имеет смысла. Для логических операций операнды и результаты имеют тип boolean. Логические операции &, |, ^ и ! поддерживают основные логические действия И, ИЛИ, исключающее ИЛИ и НЕ в соответствии с таблицей истинности, представленной в табл. 2.7.

Таблица 2.7. Таблица истинности для базовых логических операций Java p

false true false true

q

false false true true

p & q false false false true

p | q false true true true

p ^ q false true true false

!p true false true false

В табл. 2.7 видно, что результатом выполнения операции исключающего ИЛИ будет true, когда один и только один операнд равен true. В следующей программе демонстрируется работа нескольких операций отношения и логических операций. // Демонстрация работы операций отношения и логических операций. class RelLogOps { public static void main(String[] args) { int i, j; boolean b1, b2; i = 10; j = 11;

Глава 2. Введение в типы данных и операции if(i if(i if(i if(i if(i if(i

}

}

85

< j) System.out.println("i < j"); = j) System.out.println("Не выполнится."); > j) System.out.println("Не выполнится.");

b1 = true; b2 = false; if(b1 & b2) System.out.println("Не выполнится."); if(!(b1 & b2)) System.out.println("!(b1 & b2) дает true"); if(b1 | b2) System.out.println("b1 | b2 дает true"); if(b1 ^ b2) System.out.println("b1 ^ b2 дает true");

Вот вывод, генерируемый программой: i < j i > > & == ^ | && || ?: -> =

-

>>> >=

1 0 0 ) с = d; e l s e а = с ; / / э т о т оператор e l s e о т н осит ся к i f ( k > 1 0 0 ) else а = d ;

/ / э т о т оператор e l s e о т носится к i f ( i == 1 0 )

Как видно в комментариях, финальный оператор else не связан с i f ( j < 2 0 ) , поскольку он не находится в том же блоке (несмотря на то, что он - ближай­ ший if без e l s e ) . Взамен финальный оператор e l s e связан с if ( i 10) . Внутренний оператор e l s e относится к i f ( k > 1 0 0 ) , потому что он - бли­ жайший if в том же блоке. С помощью вложенного оператора i f можно улучшить игру в угадывание буквы. Следующее дополнение программы выдает игроку подсказку о том, где находится правильная буква, в случае ввода некорректного предположения. ==

/ / Игра в угадывание буквы, версия 3 . c l a s s Gue s s З { puЫ i c s t a t i c void rna i n ( S t r i n g [ ] a r g s ) throws j ava . i o . I OExcep t i o n { char ch , answer = ' К ' ; S y s tern . ou t . p r i n t l n ( " Зaдyмaнa буква между А и Z . " ) ; S y s t ern . ou t . p r i n t ( " Пo пpoбyйтe ее угада т ь : " ) ; ch = ( ch a r ) S y s t ern . i n . read ( ) ; / / чте ние символа с клавиатуры i f ( ch == a n s we r ) S y s tern . ou t . p r i n t l n ( " * * Правильно * * " ) ;

else { System . ou t . pr i n t ( " . . . Yвы , не угадали . Задуманная буква находит ся " ) ;

Вложенный оператор if 1

L_.

/ / Вложенный оператор i f . i f ( ch < a n s we r ) S y s t em . ou t . p r i n t l n ( " дaльшe по алфавиту . " ) ;

e l s e S y s tem . ou t . p r i n t l n ( " ближe по алфавиту . " ) ;

Ниже показан результат пробного запуска программы: Задумана буква между А и Z . Попробуйте ее угадат ь : z . . . Увы, не угадали . Задуманная буква находится ближе по алфавиту .

Ц епоч ка if-else - i f Распространенной программной конструкцией , основанной на последова­ тельности вложенных операторов i f, является цепочка i f-else-i f, которая вы­ глядит следующим образом: if ( условие ) опера тор ;

e l s e i f ( условие ) опера тор ;

e l s e i f ( условие ) опера тор ;

else опера тор ;

Условные выражения вычисляются сверху вниз. Как только обнаружено ис­ тинное условие , выполняется ассоциированный с ним оператор, а остальная часть цепочки игнорируется. Если ни одно из условий не выполняется, тогда будет выполнен последний оператор e l s e . Финальный оператор e l s e действу­ ет как условие по умолчанию; т.е. если все другие условные проверки не прой­ дены, то выполняется последний оператор e l s e . Если финального оператора else не предусмотрено и все остальные условия ложны, тогда никакие действия не предпринимаются. В показанной далее программе демонстрируется исполь­ зование цепочки i f-else-if. / / Демо нстрация испол ь з о вания цепочки i f - e l s e - i f . c l a s s Ladder { puЫ i c s t a t i c v o i d ma i n ( S t r i n g [ ] a r g s ) { int х ; f o r ( x= O ; х< б ; х + + ) { i f ( x== l )

. . . . . . Гл.а в(] З . ,() пЕЭраторы. упр_а влеt-1ия пр_о гр(]/-:'М о й S y s t em . ou t . p r i n t l n e l s e i f ( x==2 ) S y s t em . out . p r i n t l n e l s e i f ( x== З ) S y s t e m . ou t . p r i n t l n e l s e i f ( x==4 ) S y s tem . ou t . p r i n t l n else S y s t em . out . p r i n t l n

.

. .

1. ();3

( " Знaчeниe х равно 1 " ) ; ( " Знaчe ниe х равно 2 " ) ; ( " Знaчeниe х равно 3 " ) ; ( " Знaчeниe х равно 4 " ) ; ( " Знaчeниe х не находится ._-- О ператор, между 1 и 4 " ) ;

заданный по умолчанию

Программа производит следующий вывод: Зн ачение Зн ачение Значе ние Значение Зн ачение Значение

х х х х х х

не находит с я между 1 и 4 равно 1 равно 2 равно 3 равно 4 не находит с я между 1 и 4

Как видите, заданный по умолчанию оператор e l s e выполняется только в том случае, если не бьm выполнен ни один из предшествующих операторов i f.

Трад и цио н н ы й опера т ор swi tch Вторым оператором выбора в Java является s w i t ch , который обеспечивает многовариантное ветвление. Таким образом, он позволяет в программе выбирать из нескольких альтернатив. Хотя с помощью последовательности вложенных операторов i f можно выполнять многовариантные проверки, применение опе­ ратора swi t ch во многих ситуациях оказывается более эффективным подходом. Прежде чем продолжить, необходимо отметить один важный момент. Начи­ ная с JDK 14, оператор switch бьm значительно улучшен и расширен рядом но­ вых возможностей, выходящих далеко за рамки его традиционной формы. Из­ за значимой роли недавних усовершенствований switch они описаны в главе 1 6 в контексте других недавних дополнений к Java. Здесь оператор switch пред­ ставлен в своей традиционной форме, которая бьmа частью Java с самого начала и потому используется весьма широко. Кроме того, эта форма будет функци­ онировать во всех средах разработки Java. Рассмотрим, как работает традици­ онный оператор swi t ch. Значение выражения последовательно проверяется на предмет соответствия со списком констант. Когда совпадение найдено, выпол­ няется ассоциированная с ним последовательность операторов. Вот как выглядит общая форма традиционного оператора swi t ch: s w i t с h ( выражение )

{

c a s e константа l : последов а тельно сть опера торов

break ;

c a s e константа 2 : последова тельность опера торов

brea k ; c a s e константа ] : последова тельно сть опер а т оров

brea k ;

defau l t : последов а тельно сть операторов

В версиях Java, предшествующих JDK 7, управляющее выражение операто­ ра switch должно давать значение типа byt e , short, i n t , cha r или перечис­ ления. (Перечисления описаны в главе 1 2 . ) Тем не менее , в настоящее время выражение также может иметь тип St ring, т.е. в современным версиях Java для управления оператором swi t ch можно применять строку. (Такой прием демон­ стрируется в главе 5 при описании типа S t ring. ) Часто выражение , управля­ ющее оператором swi t ch, представляет собой просто переменную, а не более крупное выражение. Каждое значение, указанное в операторах c a s e , обязано быть уникальным константным выражением (вроде литерального значения) . Дублировать значе­ ния в операторах ca se не разрешено. Тип каждого значения должен быть со­ вместимым с типом выражения. Последовательность операторов в de faul t выполняется, если ни одна кон­ станта case не соответствует выражению. Оператор de fault необязателен; если он отсутствует, а совпадения не обнаружены, то никакие действия не предпри­ нимаются. Когда совпадение с case найдено, ассоциированные с ним операто­ ры выполняются до тех пор, пока не встретится оператор break, либо в случае de fault или последнего case - пока не будет достигнут конец swit ch. В следующей программе демонстрируется использование оператора swi tch: 11 Демо нстрация исполь зования оператора s w i t c h . c l a s s S w i t ch Demo { puЫ i c s t a t i c void m a i n ( S t r i ng [ ] a r g s ) { int i ; for ( i=O ; i < l O ; i++ ) switch ( i ) { case О : S y s tem . ou t . p r i n t l n ( " i равно 0 " ) ; brea k ; case 1 : S y s t em . out . p r i n t l n ( " i равно 1 " ) ; b re a k ; case 2 : S y s t em . out . p r i n t l n ( " i равно 2 " ) ; brea k ; case 3 : S y s tem . o ut . p r i n t l n ( " i равно 3 " ) ; brea k ;

. . . . . . Глава З . _() п�раторы упr.а .влеt-1ия . пr.о г р(]�М о й

.

. .

1 .()5.

case 4 : S y s t e m . ou t . p r i n t l n ( " i равно 4 " ) ; break; defau l t : S ys tem . ou t . p r i n t l n ( " i равно 5 или больше " ) ;

Ниже показан вывод, генерируемый программой: i i i i i i i i i i

равно равно равно равно равно равно равно равно равно равно

о 1

2 3 4 5 5 5 5 5

или или или или или

больше больше больше больше больше

Как видите, каждый раз в цикле выполняются операторы, связанные с кон­ стантой в ca s e , которая совпадает со значением i, а все остальные пропуска­ ются. После того, как значение i становится равным или больше 5, операторы case не дают совпадения, поэтому выполняется оператор default . Формально оператор break необязателен, хотя он используется в большин­ стве случаев применения swi tch. Когда внутри последовательности операторов, ассоциированной с c a s e , встречается оператор break, поток выполнения вы­ ходит из всего оператора swi tch и возобновляет работу, начиная со следующего оператора после swi tch. Однако если оператор break не завершает последова­ тельность операторов, связанную с c a s e , то все операторы в соответствующем case и после него будут выполняться до тех пор, пока не встретится break (или не закончится swi t ch). Таким образом, case без break будет проходить прямо на следующий case. Внимательно взгляните на следующую программу. Сможете ли вы предпо­ ложить, что она отобразит на экране? 1 1 Демонстрация работы s w i t c h без операторов b r e a k . c l a s s NoB r e a k { puЫ i c s t a t i c void ma i n ( S tring [ ] args ) { int i ; for ( i= O ; i= num) b r e a k ; / / прекра тить выполнение цикла if i * i >= 1 0 0 S y s t em . out . p r i n t ( i + " " ) ; S y s t em . out . p r i n t l n ( " Цикл завершен . " ) ;

Программа генерирует следующий вывод: О 1 2 3 4 5 6 7 8 9 Цикл завершен .

Хотя цикл for предназначен для выполнения от О до num (в данном случае 1 00 ) , оператор b r e a k приводит к его преждевременному завершению, когда значение i в квадрате больше или равно num. Оператор break можно использовать с любыми циклами Java, включая на­ меренно реал изованные бесконечные циклы. Например, ниже показана про­ грамма, в которой производится чтение пользовательского ввода, пока не будет введена буква q: 1 1 Чтение пол ь зователь ского в в ода , пока не будет получена буква q . c l a s s Break2 { puЫ i c s t a t i c void main ( S t r i n g [ ] a r g s ) throws j ava . i o . IOExcept i o n { char ch ; Работа этого 11бесконечного11 цикла for ( ; ) { ..----- прекращается с помощью break ch = { ch a r ) S y s t em . i n . read ( ) ; i f ( ch == ' q ' ) brea k ;

/ / п олучить символ

S y s t em . out . p r i n t l n ( " Былa нажа т а клавиша q ! " ) ;

При использовании внутри набора вложенных циклов оператор break про­ изводит выход только из самого внутреннего цикла, например: / / Испол ь з о в ание b r e a k с вложенными циклами . class BreakЗ { puЫ i c s t a t i c void ma i n ( S t r i n g [ ] a r g s ) { for ( int i=O ; i < З ; i++ ) { S y s t em . ou t . p r i n t l n ( " Cчe тчик внешнего цикла : " + i ) ; S y s t em . ou t . p r i n t ( " Сче тчик внутреннего цикла : " ) ; int t = О ; whi l e ( t < 1 0 0 ) { i f ( t == 1 0 ) brea k ; / / прекратить выполнение цикла , е сли t равно 1 0 S y s t em . out . pr i n t ( t + " " ) ; t++; S y s t em . o ut . p r i n t l n ( ) ;

. . . . . " . . . . . . . . . . . . " . . " " Глава } . О пЕЭрат() ры_ упр_а вле11ия _прогр(]flЛМО Й . J �;з S y s tem out p r i n t l n ( " Циклы завершены . " ) ; .

.

Вот вывод, генерируемый программой: Счетчик внешнего цикла : Счетчик внутре ннего Счетчик внешнего цикла : Счетчик внутре ннего Сче тчик внешнего цикла : Счетчик внутре ннего Циклы зав ершены .

о цикла : о 1 2 3 4 5 6 7 8 9 1 цикла : о 1 2 3 4 5 6 7 8 9 2 цикла : о 1 2 3 4 5 6 7 8 9

Как видите , оператор break во внутреннем цикле приводит к прекращению только этого цикла, не затрагивая внешний цикл. С оператором break связаны еще два момента, о которых следует помнить. Во-первых, в цикле могут находиться более одного оператора b r e a k. Однако будьте осторожны. Слишком большое количество операторов break способно деструктурировать код. Во-вторых, оператор break, завершающий switch, вли­ яет только на этот оператор swi tch, но не на любые объемлющие циклы.

И спол ьзова н и е оператора break ка к р азновидности перехода в сти л е "goto" В дополнение к применению с оператором s w i t c h и циклами оператор break также может использоваться сам по себе , чтобы обеспечить "цивилизо­ ванную" форму перехода в стиле "goto" . В языке Java нет оператора "goto" , т.к. он дает возможность ветвления произвольным и неструктурированным обра­ зом , что обычно затрудняет понимание и сопровождение кода, опирающегося на переходы в стиле "goto" . Тем не менее, есть несколько мест, где переходы в стиле "goto " будут ценной и законной конструкцией для управления потоком. Например, переход в стиле "goto" может быть полезен при выходе из глу­ боко вложенных циклов. Для обработки таких ситуаций в Java определена рас­ ширенная форма оператора break. С применением такой формы break можно, например, выходить из одного или нескольких блоков кода, которые не обяза­ тельно должны являться частью цикла или switch, а могут быть любыми. Более того, можно точно указывать, где будет возобновлено выполнение , т. к. расши­ ренная форма оператора break работает с меткой. Как вы увидите , break обе­ спечивает преимущества перехода в стиле "goto " без присущих ему проблем. Общая форма оператора break с меткой выглядит следующим образом: b r e a k метка ;

Обычно метка представляет собой имя маркера, идентифицирующего блок кода. При выполнении расширенной формы оператора b r e a k поток управ­ ления покидает блок, указанный в b r e a k. Снабженный меткой блок должен

охватывать оператор break, но не обязательно быть блоком, который содержит в себе этот b r e a k непосредственно. Отсюда следует, например, что оператор break с меткой можно использовать для выхода из набора вложенных блоков. Но применять break для передачи управления из блока, который не охватывает данный оператор break, нельзя. Чтобы назначить блоку имя, необходимо поместить в его начало метку. По­ мечаемый блок может быть автономным блоком или оператором, телом кото­ рого является блок. Метка это любой допустимый идентификатор Java, за которым следует двоеточие. После пометки блока метку можно использовать в качестве цели оператора break, что приведет к возобновлению выполнения по­ сле конца помеченного блока. Например, в показанной далее программе реали­ зованы три вложенных блока: -

11 Испол ь з о в ание оператора b r e a k с мет кой . class Break4 { puЫ i c s t a t i c void ma i n ( S t r i n g [ ] a r g s ) { int i ; for ( i=l ; i ' 7 ' & cho i c e

!=

'q' ) ;

Обратите внимание, что цикл теперь включает операторы b r e a k и continue, а также принимает букву q в качестве допустимого варианта. 4.

Расширьте оператор s w i t c h , добавив вывод сведений об операторах

break и continue: case ' 6 ' : S y s t em . ou t . p r i n t l n ( " Oпepaтop b r e a k : \ n " ) ; S y s tem . o u t . p r i n t l n ( " b r ea k ; или b r e a k ме т ка ; " ) ; brea k ; case ' 7 ' : S y s t em . ou t . p r i n t l n ( " Oпepaтop continue : \ n " ) ; S y s t em . ou t . p r i n t l n ( " continue ; или continue метка ; " ) ; bre a k ;

5. Н иже показан полный код проrраммы НеlрЗ . j ava: /* Упражне ние 3 . 3 . Законченная справочная системы по операторам Java , позволяющая обрабатыв а т ь множе с т в о запросо в . */ c l a s s НеlрЗ { puЫ i c s t a t i c void ma i п ( S t r i ng [ ] throws j ava . i o . I OException char cho i ce ,

args )

ignore ;

for ( ; ; ) do { S y s t em . out . p r i n t l n ( " Cпpaвкa по : ) ; 1 . if" ) ; S y s t em . out . p r i n t l n ( " 2 . switch" ) ; S y s t em . out . p r i n t l n ( " 3 . for" ) ; S y s t em . out . p r i n t l n ( " 4 . wh i l e " ) ; S y s t em . out . p r i n t l n ( " 5 . do-wh i l e " ) ; S y s t em . ou t . p r i n t l n ( " 6 . break " ) ; S y s t em . out . pr i n t l n ( " S y s t em . out . p r i n t l n ( " 7 . continue \ n " ) ; System . out . print ( " Bыбepитe вариант ( или q для завершения ) : " ) ; 11

cho i c e

( ch a r )

S y s t em . i n . read ( ) ;

do

{ ignore = ( ch a r ) S y s t em . i n . read ( ) ; ) whi l e ( ignore ! = ' \ n ' ) ; wh i l e ( cho i c e < ' 1 ' 1 choice > ' 7 ' & choice i f ( ch o i c e ==

'q' )

!=

'q' ) ;

break ;

S y s t em . ou t . p r i n t l n ( " \ n " ) ; s w i t ch ( choice ) case ' 1 ' : S y s t em . o u t . p r i n t l n ( " Оператор i f : \ n " ) ; S y s t em . ou t . p r i n t l n ( " i f ( ycлoвиe ) оператор ; " ) ; S y s tem . out . p r i n t l n ( " e l s e оператор ; " ) ; break; case ' 2 ' : S y s t em . ou t . p r i n t l n ( " Tpaдициoнный оператор s w i t ch : \ n " ) ; S y s t em . ou t . p r i n t l n ( " s w i t ch ( выpaжeниe ) { " ) ; S y s t em . out . p r i n t l n ( " c a s e константа : " ) ; S y s t em . o u t . p r i n t l n ( " п о следователь н о с т ь операторов " ) ; S y s t em . ou t . p r i n t l n ( " break; " ) ; S y s t em . ou t . p r i n t l n ( " // . . . ") ; S y s t em . ou t . p r i n t l n ( " I " ) ; break ; case ' 3 ' : S y s t em . ou t . p r i n t l n ( " Цикл f o r : \ n " ) ; S ystem . out . print ( " fo r ( инициaлизaция ; условие ; итерация ) " ) ; S y s t em . ou t . p r i n t l n ( " оператор ; " ) ; break;

. . . . " _ Гл.а_� (] 3: _С? n е� рат() р_ы. У П.Р.