Java истории языка, Обзор Основных возможностей, задачи и решения


Учебное пособие для студентов, обучающихся по специальностям «Информатика», «Программная инженерия», «Компьютерные науки», «Прикладная математика»

Содержание

Раздел 1.1. Краткий очерк истории языка

      1. С чего все начиналось
      2. Зеленый дуб и чашечка кофе
      3. Первые разочарования и переломный момент
      4. Важные вехи и версии Java

Раздел 1.2. Основные свойства языка Java

      1. Канонический список параметров
      2. простота
      3. объектная ориентированность
      4. распределенность
      5. надежность
      6. безопасность
      7. Независимость от архитектуры компьютера
      8. переносимость
      9. интерпретируемость
      10. производительность
      11. многопоточность
      12. динамичность

Раздел 1.3. Кросс-платформнисть, интерпретация и компиляция

1.3.1. Традиционная схема выполнения программ 1.3.2.Виртуальная машина и байт-код

1.3.3. Интерпретация и JIT-компиляция

Раздел 1.4. проблема защиты

      1. Уровни защиты Java-программ
      2. Понятие о менеджер безопасности 1.4.3.модель песочнице

1.4.4. Изменения в модели безопасности

Раздел 1.5. Технологии, платформы, программные модели

      1. Общий обзор Java-технологий
      2. программные модели
      3. Java и .NET

Раздел 1.6. Веб-страница Java

контрольные вопросы

ЧАСТЬ 2. НАПИСАНИЕ ПРОСТЕЙШИХ консольных приложений

Раздел 2.1. Основные средства для создания Java-приложений

2.1.1 Обзор инструментов для написания Java-программ

2.1.2. Основные утилиты JDK

2.1.3.Компиляция и выполнение программ средствами JDK

      1. Настройка переменных окружения
      2. Программа с несколькими классами
      3. опция -classpath
      4. Создание Java-приложений средствами Eclipse
      5. Создание Java-приложений средствами NetBeans
      6. Знакомство с дизассемблеры.

Раздел 2.2. Анатомия простого применения

      1. основной класс
      2. Класс и файл: названия могут не совпадать
      3. Метод main и его варианты
      4. Варианты Hello, world
      5. Аргументы командной строки
      6. Программа без метода main
      7. Понятие о пакетах

Раздел 2.3. Операции ввода-вывода

      1. Общее понятие о потоках ввода-вывода
      2. Стандартные потоки ввода-вывода
      3. ввод символов
      4. Классическая схема ввода строк
      5. Использование конкатенации для вывода
      6. Ввод данных других типов
      7. Более новые возможности для ввода и вывода
      8. форматированный вывод
      9. Использование класса Scanner
      10. Некоторые особенности и сюрпризы класса Scanner
      11. Использование диалоговых окон для ввода
      12. Класс Console Контрольные вопросы

Часть 3. ОБЗОР ОСНОВНЫХ ВОЗМОЖНОСТЕЙ ЯЗЫКА

Введение

Раздел 3.1. Основные процедурные черты

3.1.1. Комментарии 3.1.2.Переменные и выражения

      1. Важнейшие типы данных
      2. константы
      3. предоставление значения
      4. Основные операции Java
      5. Оператор или инструкция?
      6. составленные присваивания
      7. Инкременты и декременты
      8. условные операторы
      9. тернарная операция
      10. циклы
      11. Циклы: сначала думаем, потом коды
      12. оператор варианта
      13. функции
      14. строки

Раздел 3.2. Классы и объекты

      1. простейший пример
      2. Селекторы и модификаторы
      3. дальнейшее развитие
      4. наследование

        3.2.5.Пример: наследование метода main ()

      5. класс Object
      6. Элементарный демонстрационный класс
      7. Метод equals ()
      8. Метод toString ()
      9. «Канонический» состав класса
      10. Использование IDE для автоматического конструирования классов
      11. интерфейсные типы
      12. перечисление
      13. исключение

Раздел 3.3. Примитивные и объектные типы

      1. Разница между примитивными и объектными типами
      2. Есть ли в Java указателей
      3. Низкоуровневая реализация списка
      4. Когда x instanceof Object равен false

Раздел 3.4. Наборы данных: массивы и коллекции

      1. Понятие о наборах данных
      2. Основные черты массивов
      3. Знакомство с классом Arrays: сортировка массивов
      4. Динамическое заполнение массивов
      5. Массив из экземпляров класса
      6. Проблема с размером массивов
      7. Переход к коллекциям
      8. Знакомство с многомерными массивами Контрольные вопросы

Часть 4. АНАЛИЗ ЭФФЕКТИВНОСТИ, отладки и тестирования

Раздел 4.1. отладки программ

      1. Общее представление о отладки
      2. Простейшие приемы отладки
      3. Использование протоколирования для отладки
      4. Инструментальные средства отладки

Раздел 4.2. тестирование программ

      1. Роль тестирования в программном проекте
      2. Знакомство с JUnit

Раздел 4.3. Качество и эффективность программ

      1. Понятие о качестве программного обеспечения
      2. Основные показатели эффективности
      3. Исследование времени выполнения фрагментов кода
      4. Пример: сравнение двух способов получения строки из примитивного типа
      5. Пример: сравнение интерпретации и синхронной компиляции
      6. Понятие о Java Code Conventions Контрольные вопросы

Упражнения на программирование Перечень листингов программ

Предисловие

Сегодня Java является одной из наиболее популярных языков программирования. Она получила широкое распространение в индустрии программного обеспечения как одна из основных платформ для создания распределенных корпоративных систем и мобильных приложений. Считается, что это одна из основных языков, которую должен знать современный специалист по IT-технологиям.

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

Существует много хороших книг по Java, в первую очередь двухтомник К.Хорстманна и Г.Корнелла «Java. Библиотека профессионала «и Б.Еккеля» Философия Java «. Но все-таки эти книги трудно рассматривать как классические учебники. Они ориентированы скорее на более или менее грамотных программистов, чем на студентов-новичков. Не хватает развернутых учебных примеров, иллюстрирующих типичные ошибки, последствия этих ошибок и процесс их последовательного исправления. Не хватает примеров, направленных на сравнительный анализ различных способов решения той или иной задачи. С другой стороны, существуют книги, специально посвященные тонкостям и подводным камням — но им не хватает систематического изложения основного материала. Недостаточное внимание уделяется инструментальным средствам — таким, как JUnit, средства отладки и автоматической генерации кода и тому подобное. Практически не упоминается дизассемблер — а в то же время анализ байт-кода может стать мощным фундаментом для более глубокого понимания ряда особенностей языка (конструирование классов, ковариантность и т.д.).

Поэтому планируется написание серии учебных пособий и учебников по Java, направленных на восполнение этого пробела. Это пособие — это первая книга этой серии; она посвящена основным возможностям языка.

В книге на интуитивном уровне рассматриваются базовые черты языка, необходимые для того, чтобы начать программировать на Java: организация типичного консольного приложения, ввода-вывода, основные алгоритмические конструкции (циклы, условные операторы и т.д.), основы объектно-ориентированного программирования (строение типичного класса, основные понятия о наследовании), интерфейсные типы, перечисления, исключения, строки, массивы. После обсуждения проблем, связанных с массивами, вводится понятие коллекции как более высокоуровневого набора данных; приводится соответствующий пример. Рассматриваются основные утилиты SDK, в том числе дизассемблер: средства хронометрирования программ, а также средства отладки и тестирования — в частности, JUnit.

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

К каждой части приводятся контрольные вопросы; в конце пособия содержится перечень упражнений на программирование.

Руководство предполагает определенный предыдущий уровень знаний процедурного и объектно-ориентированного программирования.

ЧАСТЬ 1. БАЗОВЫЕ ПРИНЦИПЫ ЯЗЫКА JAVA

Раздел 1.1. Краткий очерк истории языка

      1. С чего все начиналось

        Язык Java разрабатывалась под эгидой корпорации Sun Microsystems с 1991 года. Что к этому побудило? Ведь уже в 70-х годах прошлого века существовало огромное количество языков программирования, и для создания новых языков нужны были реальная необходимость и новая сильная идея.

        Итак, далекий 1990 год. Многие нынешние программистов еще не родились. Среди языков программирования доминируют С и С ++; для повышения эффективности ряд фрагментов кода приходится писать на ассемблере. Не так давно появилась, но уже уверенно заявила о себе новая в то время парадигма объектно-ориентированного программирования. Только начинается массовое распространение Интернета. Графические пользовательские интерфейсы и технологии мультимедиа еще находятся на начальном этапе развития.

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

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

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

        Было принято нестандартное решение — отказаться от применения С ++ и создать новый язык программирования. Так родился проект Green.

      2. Зеленый дуб и чашечка кофе

        Лидерами и основными разработчиками проекта Green, направленного на создание нового языка, стали Патрик Ноутон и Джеймс Гослинг. К этому языку были выдвинуты очевидны требования:

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

        Сначала новый язык называлась Oak (дуб) — в честь дуба, который рос под окнами офиса Гослинга. Потом выяснилось, что язык программирования с таким названием уже существует, и новый язык переименовали в Java.

        Название Java тоже достаточно показательна. Java (Ява) — это название сорта кофе, который выращивается на индонезийском острове Ява. Этот остров, как и вся Индонезия, лежит на границе Индийского и Тихого океанов. В американском сленге Java — это название кофе вообще. Поэтому символ языка Java, который любят изображать на пособиях — это чашка кофе, над которой бурлит дымок.

        Варианты здесь самые разные — от откровенно минималистского фирменного логотипа до аппетитных натуралистических фотографий и рисунков.

        java-logo_350_250.jpg

        Рис.1.1. Чашечка кофе — символ Java

        Интересное для восточного славянина вопрос — как правильно называть этот язык: Ява или Ява? Считается правильным произношение Ява; именно на ней настаивает компания Sun. Но и против названия Ява мало кто отрицает — в конце концов, остров все-таки называется Явой.

      3. Первые разочарования и переломный момент

        Первая разработка в рамках проекта Green под названием «* 7» была выпущена в 1992 году.

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

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

        Так бывает со многими гениальными идеями — если идея не играет в одном месте, она может сыграть в другом. Успех пришел не с той стороны, откуда его ожидали в начале

        • он был связан с Интернет-технологиями. Поэтому иногда можно услышать, что язык Java разрабатывалась для создания сетевых и веб-приложений, но это не так. Изначальной целью было программирования бытовых устройств, но все же …

          Мы знаем, что девяностые годы ознаменовались бурным развитием Интернета, в частности World Wide Web. За короткие 10 лет Интернет превратился из чисто технического явления, о котором известно лишь небольшому кругу специалистов, на грандиозный социальный феномен, который во многом определяет нынешнее развитие человечества.

          Но тогда был еще 1994 год. Компания Sun сначала пропустила этот рывок, но быстро поняла, что все еще можно наверстать. Родилась идея создания апплетов.

          Апплет — это программный модуль, написанный на языке Java, который выполняется в среде браузера.С помощью специального тега ссылки на класс апплета включается в веб-страницы. При загрузке страницы браузер загружает и сам апплет.

          Оказалось, что язык Java идеально подходит для этой цели. Два основных фактора способствовали этому:

          • интерактивность, то есть возможность взаимодействия с пользователем. В отличие от реалий, тогда и сама такая возможность была диковинкой — а апплеты на

            дополнение к развитых средств пользовательского интерфейса реализовывали еще и интерактивную анимацию;

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

            Для демонстрации этой технологии требовался специальный браузер, который и был разработан (он получил название HotJava). Апплеты были продемонстрированы на выставке Sun World’95 в мае 1995 года, и это имело огромный успех.

            При всем уважении к апплетов следует отметить, что сегодня эта технология уходит в прошлое. Возникли большие проблемы с применением апплетов (не было гарантии, что в браузере установлена ​​поддержка современной версии Java, которая поддерживает новые возможности графического интерфейса). Сегодня для доставки приложений на клиентский компьютер предлагается другая технология — Java Web Start. Но в 1995 году апплеты свое дело сделали — и началось триумфальное распространение Java.

      4. Важные вехи и версии Java

Первая версия Java (Java 1.0) была выпущена в начале 1996 Вместе с ней был выпущен и комплект для разработки приложений — Java Developer’s Kit (JDK).

Эта версия, по общему признанию, была еще очень сырой. Несовершенной была система ввода-вывода; модель обработки событий в графическом интерфейсе тоже была очень неудачной. Некоторые исправления были сделаны в версии 1.1, но настоящей революцией в мире Java стал выпуск Java 1.2 (1998 г.). Эта версия стала называться Java 2 — так же, как и ее ближайшие преемники.

Изменений в самой речи не было, но появились новые библиотеки, и в первую очередь — библиотека Swing для программирования графического пользовательского интерфейса, которая является основным средством для создания desktop-приложений и сегодня. Было осуществлено разделение на три платформы Java 2:

  • Standard Edition — стандартный пакет;

  • Enterprise Edition — пакет для корпоративных приложений;

  • Micro Edition — пакет для портативных устройств (в частности, для мобильных телефонов).

    Новая революция произошла в 2004 г.. — версия Java 1.5 (или Java 5).

    К сожалению, то, что происходило с названиями версий Java, трудно охарактеризовать иначе как творческий беспорядок. Стандартный пакет инструментальных средств от Sun назывался то JDK, то SDK. Версии Java 5 и Java 6 могли называться соответственно 1.5 и 1.6, а как правильно — мало кто знает. Впрочем, всегда было понятно, о чем идет речь.

    Вернемся к Java 5 (или Java 1.5). В этой версии произошли существенные изменения в самом языке. Самые заметные из них — это generics (обобщенные, или параметризованные типы), модифицированный for — удобный цикл для просмотра всех элементов массива или коллекции. Было много других нововведений, на которые мы будем обращать внимание на протяжении всей книги.

    Версия Java 6, или Java 1.6 (2006 г.), Не внесла изменений в речи; были добавлены новые классы.

    Только в 2011 году вышла долгожданная Java 7. За это время сама компания Sun была приобретена компанией Oracle, но ее основные проекты остались. В этой версии состоялся ряд синтаксических изменений — но не все из тех, которые были анонсированы.

    Наконец, весной 2014 появилась Java 8, которая тоже внесла ряд языковых новаций.

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

    На протяжении всей книги мы будем постоянно обращать внимание на изменения, которые делались в Java 5, Java 7 и Java 8.

    Раздел 1.2. Основные свойства языка Java

        1. Канонический список параметров

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

  • простота;
  • объектная ориентированность;
  • распределенность;
  • надежность;
  • безопасность;
  • независимость от архитектуры;
  • портабельнисть;
  • интерпретируемость;
  • производительность;
  • многопоточность;
  • динамичность.

    Дадим некоторые комментарии в этот перечень.

        1. простота

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

          Язык C ++ имеет в своем распоряжении огромный арсенал разнообразных возможностей. Но такое разнообразие требует уверенного владения соответствующими языковыми средствами и грамотного их использования. Разработчики Java исключили те черты С ++, которые, по их мнению, является или редко используемыми и трудными в изучении, или концептуально сомнительными или опасными с точки зрения возможных программных ошибок.

          Перечислим некоторые упрощения Java по сравнению с C ++:

  • в Java нет множественного наследования классов. Вообще, даже одиночное наследование далеко не всегда используется правильно, а с множественным наследованием в С ++ даже при грамотном применении связано много концептуальных проблем;
  • из различных вариантов одиночного наследования классов, присущих С ++, в Java осталось только открытое наследования;
  • в С ++ переменные можно размещать или на стеке выполнения программы, или в динамической памяти. В Java такой свободы нет: экземпляры классов размещаются в куче, а переменные примитивных типов — на стеке;
  • в С ++ имеются широкие возможности для передачи параметров функций: либо по значению, либо по ссылке, либо через указателей. В Java параметры функций всегда передаются по значению;
  • в отличие вех С ++, если поле класса в Java имеет объектный тип, оно может быть лище указателей на экземпляр. В соответствии с этим механизмы конструирования объектов в Java могут быть более простыми, чем в C ++; исчезает особая потребность в копировальных конструкторах (которых в Java в явном виде и нет);
  • в С ++ много программных ошибок связано с ненадлежащим уничтожением объектов. В Java сбора мусора (то есть уничтожение объектов, которые больше не используются), осуществляется автоматически, а операция явного удаления (delete) объектов вообще отсутствует;
  • в Java нет возможности определять операции в классах; механизм реализации операций мог бы быть более простым, чем в C ++, но разработчики Java от этого отказались;
  • в Java нет операций взятия адреса, разыменования и арифметики указателей учитывая значительные риски, связанные с их неправильным использованием;
  • в Java нет модификатора virtual; в зависимости от их описания методы класса являются или полиморфными, или нет; управлять этим нет возможности.

    С другой стороны, Java предоставляет некоторые новые возможности, которых нет в С ++. В частности, в явном виде поддерживается концепция интерфейсов. Расширяются возможности, связанные с внутренними классами и с обработкой исключений (секция finally, которая выполняется независимо от того, было исключение или нет). На уровне языка поддерживается многопоточность. В Java есть развитые средства рефлексии, то есть анализа классов на этапе выполнения.

    Конечно, вопрос о том, что Java имеет полностью заменить C ++, не ставится — каждая из этих языков имеет свою нишу и свою область применения.

        1. объектная ориентированность

          В 1991 году объектно-ориентированная парадигма уже считалась магистральной, и поэтому Java просто обязана была стать объектно-ориентированным языком. На этом можно было бы и остановиться, но между объектными чертами C ++ и Java есть ряд серьезных отличий.

          Часто говорят, что Java является полностью объектно-ориентированным. Если в С ++ мы не хотим использовать классы, а хотим написать программу в чисто процедурном стиле, мы можем это сделать. В Java это не так. Программный код всегда должно быть инкапсулированный в классы. Таким образом, любая программа, даже самая простая, должна быть оформлена как класс (один или несколько).

          Сразу же рассмотрим элементарный пример — программу, которая выводит на экран строку Hello, world.Если это все, что нужно — это в объектно-ориентированном анализе, проектировании и программировании для написания такой программы, конечно, нет никакой необходимости. По своей сути программа чисто процедурная, но в Java мы не можем обойтись без классов — пока считаем это необходимым требованием синтаксиса.

          Код может иметь вид:

          public class HelloWorld {

          public static void main (String [] args) {

          System.out.println ( «Hello, world!»)

          }

          }

          Листинг 1.2.3.1. Hello, world: даже самая простая программа требует создания классов

          Отложим более детальный анализ этой программы на более позднее время; сейчас нужно сказать об этом.

          Мы видим один класс, который называется HelloWorld. Любая функция — это метод класса; никаких переменных или функций за пределами классов в Java быть не может. В дальнейшем мы будем считать термины «функция» и «метод (класса)» синонимичны, и употреблять термин «функция», если нужно будет оттенить процедурные аспекты языка, и «метод» — если объектно-ориентированные.

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

          Объектные черты Java в своей основе соответствуют общепринятым канонам объектно-ориентированного программирования. Они в целом похожи на аналогичные черты С ++, но есть ряд существенных отличий; о некоторых из них речь шла в предыдущем разделе.

        2. Распределенность.

          Уже Java Standard Edition имеет много средств для сетевого взаимодействия (в частности, чтение веб-ресурсов, обмен данными с удаленными серверами, поддержка сокетов и т.п.), а Enterprise Edition и Micro Edition непосредственно ориентированы на создание корпоративных и мобильных приложений, которые являются распределенными по своей природе . Поэтому Java-технологии рассматриваются как платформа для создания распределенных приложений. В этом контексте стоит вспомнить об одной из базовых технологий сетевого взаимодействия на основе Java — RMI (Remote Method Invocation).

          Но распределенность предполагает не только физическую распределенность и собственно обмен данными по сети. Не меньшее значение имеет и взаимодействие между логическими уровнями приложения — в частности, между уровнем бизнес-логики и уровнем данных в рамках трехуровневой клиент-серверной архитектуры. И здесь Java предоставляет ряд стандартных средств:

  • развитая система потоков ввода-вывода, в частности для чтения и записи файлов;
  • взаимодействие с реляционными базами данных (в Первая очередь JDBC; фактическим стандартом стал инструмент объектно-реляционного отображения Hibernate, хотя он и не входит в стандартной поставки Java)
  • чтение и модификация XML-документов.

    Кроме того, существуют пакеты для работы с другими типами данных (например, JSON).

        1. надежность

          Здесь под надежностью подразумевается защищенность программ от случайных внутренних ошибок. Как известно, основным источником программных ошибок в таких языках, как C / C ++ (и Паскаль) является некорректное обращение к памяти (связанные в первую очередь с неправильным использованием указателей и выходом за пределы массива). В Java такие ошибки практически исключены. Арифметика указателей, операции разыменования и взятия адреса в языке отсутствуют, так что напрямую обратиться к участку памяти по ее адресу невозможно (если только не вызываются процедуры в родном машинном коде). Выходы за пределы массива жестко контролируются, и при попытке обратиться к элементу массива с несуществующим индексом возникает исключение (ошибка времени выполнения).

        2. Безопасность.

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

        3. Независимость от архитектуры компьютера.

          Независимость от архитектуры, или кросс-платформнисть, напомним, была одной из основных черт, ради которой вообще создавалась Java. Это свойство означает, что откомпилированные модули, написанные на Java, должны работать на любой машине с любой архитектурой и под управлением любой операционной системы. Sun говорит по этому поводу: «Write Once — Run Anywhere» (напиши один раз — запускай где угодно).

          Механизмы достижения кросс-платформности опираются на концепции виртуальной машины и байт-кода. Была разработана спецификация виртуальной машины Java — абстрактной машины с собственным набором команд. Исходный код Java-программы компилируется в последовательность команд этой абстрактной машины (байт-код), которая интерпретируется на конкретной платформе. Более подробно это будет описано в следующем разделе.

        4. Переносимость.

          Для обеспечения переносимости (портабельности) программ по одной пдатформы на другую разработчики Java пытались реализовать принцип, согласно которому в языке не должно быть машинно-зависимых рис, или точнее — ни один аспект спецификации Java не должен зависеть от реализации.

          Классический вопрос на эту тему: размер имеет тип int в языке С? Правильным ответом считается sizeof (int), поскольку этот размер может быть разным на разных платформах. В Java оператор sizeof вообще отсутствует, и для всех стандартных типов данных размер определяется спецификацией.

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

        5. интерпретируемость

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

        6. производительность

          Интерпретация байт-кода является медленным процессом, и поэтому разработчики предусмотрели возможность преобразования отдельных фрагментов промежуточного кода в машинный код. Речь идет о механизме JIT-компиляции, который будет рассматриваться далее.

        7. многопоточность

          В Java на уровне языка поддерживаются потоки — «легкие» процессы, которые могут параллельно работать, взаимодействовать между собой в рамках одной программы и, соответственно, использовать общий адресное пространство.

        8. динамичность

    В Java сравнительно легко писать, распространять и использовать новые пакеты и библиотеки. Типичным является распространение jar-архивов.

    По сути, jar-архив — это и есть библиотека, которая содержит классы и функции для решения тех или иных задач. Такой архив может быть легко загружен, в частности из Интернета, и подключен к собственному программного проекта. Можно создавать собственные библиотеки. Стандартным инструментом для создания jar-архивов является утилита jar, которая входит в состав JDK.

    Библиотеки тесно связаны с пакетами (физически и логически объединенными группами классов), хотя эта связь не всегда столь однозначным.

    Раздел 1.3. Кросс-платформнисть, интерпретация и компиляция

        1. Традиционная схема выполнения программ

          При традиционном подходе к программированию на языках высокого уровня (Паскаль, C / C ++ и др ..) исходный код, то есть текст программы, написанный одной из таких языков, компилируется в объектный код — последовательность машинных команд, понятных процессору. Механизм разрешения компиляции позволяет компилировать различные части программы независимо. Редактор связей позволяет скомпоновать отдельные откомпилированные объектные модули в один загрузочный модуль, который может быть загружен в память и непосредственно выполнен данным процессором.

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

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

        2. Виртуальная машина и байт-код

    Требование кросс-платформности заключается в том, что откомпилированные Java-приложения должны выполняться на машинах с произвольной архитектурой и работать в любой операционной среде. В качестве основы для достижения этой цели (Write Once — Run Anywhere), была использована концепция виртуальной машины.

    Согласно классическому определению, виртуальная машина — это совокупность ресурсов, предоставляемых определенной машиной для эмуляции работы некоторой другой машины. Sun разработала спецификацию абстрактной Java-машины с определенным набором команд. Исходный код Java- программы компилируется не в «родные» машинные коды, а в промежуточный код — байт-код.

    Байт-код — это последовательность команд Java-машины.Понятно, что с одной стороны этот код является независимым от конкретной машинной архитектуры, а с другой — конкретная машина должна предоставлять средства для выполнения этого кода. Именно это и является основной задачей Java Virtual Machine (JVM) — виртуальной машины Java.

    Кроме виртуальной машины для выполнения Java-программ необходимо наличие определенного окружения, которое называется Java Runtime Environment (JRE).

    1.3.2. Интерпретация и JIT-компиляция

    JVM обеспечивает интерпретацию и выполнение байт-кода на конкретной платформе. Но классическая пошаговая интерпретация — это достаточно медленный процесс, и поэтому появился механизм JIT-компиляции(Just-In-Time-компиляция; другое название — синхронная компиляция).

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

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

    Раздел 1.4. проблема защиты

        1. Уровни защиты Java-программ

          Защищенность Java-программ — это еще один аспект, на который стоит обратить внимание.

          Рассматриваются определенные уровни защиты, а именно:

  • уровень языка: исключены потенциально опасные операции (в первую очередь — арифметика указателей) жесткий контроль за выходом за пределы массива и т.п; об этом уже говорилось в разделе 1.2.5;
  • верификация, которая осуществляется при загрузке классов.Верификация предполагает проверку правильности кода (например, отсутствие неинициализированных переменных). Стандартный компилятор Java сам контролирует подобные ошибки и должен создавать правильный байт-код, который будет успешно верифицированы. Но необходимость верификации диктуется тем, что этот код можно изменить вручную.

  • менеджер безопасности (SecurityManager), который контролирует потенциально опасные действия;

  • использование цифровых подписей.
        1. Понятие о менеджер безопасности

          Остановимся более подробно на аспектах, связанных с использованием менеджеру безопасности. Методы классов. которые отвечают за выполнение определенных потенциально опасных действий (например, запись на диск), должны обращаться к классу SecurityManager. Если данный код имеет разрешение на выполнение этой операции, работа программы продолжается; если же нет — генерируется соответствующее исключение (ошибка времени выполнения).

        2. модель песочнице

          Как базовая рассматривается модель безопасности, принятая в Java 1.0. Эта модель известна как модель sandbox (песочница). Приложения, которые выполняются под управлением операционной системы, могут осуществлять любые операции. Но апплеты, которые загружаются по сети и выполняются в среде браузера, ограничены правилами «песочнице».

          Существует несколько правил песочнице, основными из них являются следующие:

  • апплет не может обращаться к локальной файловой системы;
  • апплет не может осуществлять сетевых соединений, кроме того сервера, с которого он был загружен ( «апплет может звонить только домой»)
  • апплет не может запускать модули в машинном коде.
        1. Изменения в модели безопасности

    В моделях безопасности Java 1.1 и Java 2 произошли существенные изменения:

  • начиная с Java 1.1, «надежные», подписанные апплеты могут получать те же права, что и применения;
  • начиная Java 2, сами права становятся избирательными; для их установки используются файлы политик безопасности.

    Раздел 1.5. Технологии, платформы, программные модели

        1. Общий обзор Java-технологий

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

          Выделяются следующие платформы Java, ориентированные на различные практические применения и соответственно — на различные технологии:

  • Java Standart Edition (SE) — базовые возможности;

  • Java Enterprise Edition (EE) — библиотеки для программирования корпоративных, в частности веб-приложений;

  • Java Micro Edition (ME) — для программирования портативных устройств — например, мобильных телефонов;

  • Java Card — для устройств, которые не имеют собственного пользовательского интерфейса — например, для смарт-карт.

    Приобрела значительную популярность JavaFX — набор инструментальных средств для создания Rich Internet Applications с развитым графическим интерфейсом пользователя.

        1. программные модели

          Понятие программной модели предполагает определенный способ организации Java-приложения, то есть определенный набор ключевых методов. Например, для обычных приложений таким ключевым методом является метод main — основная точка входа в программу. При запуске программы виртуальная машина ищет этот метод и вызывает его, и с этого начинается выполнение программы. Если метод main с нужной сигнатурой (набором параметров определенного типа) не найден, возникает исключение — ошибка времени исполнения, приводит к аварийному завершению работы программы.

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

          Обычно выделяют такие программные модели:

  • application — программы, которые выполняются под управлением операционной системы; они могут быть консольными или обеспечивать графический интерфейс пользователя;
  • applet — программные модули, которые выполняются в среде браузера;
  • midlet — модули, которые выполняются на мобильных телефонах;
  • xlet — модули для портативных устройств, которые являются более мощными, чем мобильные телефоны;
  • servlet — модули, выполняемых под управлением веб-сервера или сервера приложений;
  • ejb — модули, работающие в рамках компонентной модели EJB (Enterprise Java Beans).
        1. Java и .NET

    Конкуренция между компаниями Sun и Microsoft в конце 90-х годов прошлого века привело к появлению конкурирующей платформы для создания распределенных корпоративных систем, которая развивается и поддерживается Microsoft — .NET. Основным языком .NET стала специально разработан язык программирования — C #.

    Как и Java, .NET реализует концепцию байт-кода и теоретически декларирует кросс платформнисть. Но в Microsoft не скрывают, что они развивают .NET с ориентацией

    именно на Windows. Существует альтернативный проект — Mono, ориентированный на обеспечение кросс-платформности.

    С другой стороны, характерной чертой, «фишкой» .NET является многоязычие (cross- language): возможность взаимодействия в рамках одного приложения модулей, написанных на разных языках программирования.

    Как язык программирования C # во многом похож на Java, но имеет ряд существенных отличий и во многом приближается к C ++. Поскольку C # разрабатывался позже, его разработчики имели возможность использовать опыт разработки Java. Отметим некоторые из наиболее заметных особенностей C #:

  • подобно C ++ и в отличие от Java, в C # в классах можно определять операторы;
  • подобно С ++, C # имеет модификатор virtual, который выполняет функцию виртуальной, то есть обеспечивает ее полиморфность. Кроме того, для управления виртуальностью введено модификаторы new и override(в C ++ подобные возможности отсутствуют);
  • предусмотренный модификатор unsafe (опасный), который позволяет использовать операции с указателей.

    С # имеет некоторые черты, которых нет ни в С ++, ни в Java (по крайней мере, в существующих версиях), в частности:

  • поддерживаются так называемые автореализовани свойства, то есть связанные с полями класса стандартные селекторы и модификаторы, не нужно расписывать явно;
  • существует ключевое слово для описания делегатов, которые часто (хотя и не совсем точно) называют указателей на функции;
  • в последних версиях C # реализован ряд возможностей, характерных для функционального программирования; в частности, поддерживаются ламбда-выражения — по сути упрощенные описания функций. Подобные возможности начали появляться только в Java 8.

Раздел 1.6. Веб-страница Java

Существует официальная веб-страница, посвященная Java — http://www.oracle.com/technetwork/java/index.html, более известная по своей старой адресу

  • http://java.sun.com. На этой странице можно найти две спецификации — нормативные документы для тех, кто программирует на Java и / или разрабатывает средства поддержки для программирования на этом языке.

    Спецификация Java Language Specification (JLS) определяет основные черты языка, нормативные для любой реализации. На данный момент не видно существенной альтернативы стандартной реализации от Oracle (JDK), но можно рассматривать возможность появления других реализаций.

    Спецификация Java Virtual Machine Specification (JVMS) регламентирует принципы работы виртуальной машины Java, а также содержит описание байт-кода.

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

    КОНТРОЛЬНЫЕ ВОПРОСЫ

    1. Какой была основная цель создания Java в рамках проекта Green?
    2. Почему символом Java стала чашечка кофе?
    3. Что такое апплет?
    4. Какую роль сыграла технология апплетов в становлении Java?
    5. Какой смысл обычно вкладывается в формулировку «Java полностью объектно ориентированным языком»?
    6. Охарактеризуйте понятие кросс-платформности и механизм ее достижения в Java.
    7. Что такое байт-код?
    8. Охарактеризуйте понятие JIT-компиляции. На чем основывается повышения эффективности JIT-компиляции по сравнению с классической интерпретацией?
    9. Охарактеризуйте основные механизмы обеспечения надежности и безопасности, реализованные в Java на уровне основных свойств языка.
    10. Охарактеризуйте суть верификации, которая осуществляется при загрузке класса. В чем заключается необходимость верификации?
    11. Для чего предназначен менеджер безопасности? Охарактеризуйте механизм его использования.
    12. Охарактеризуйте модель песочницы и основные ограничения на апплеты в рамках этой модели.
    13. Какие изменения произошли в Java 1.1 и Java 2 по сравнению с моделью песочнице?
    14. Перечислите основные платформы Java.
    15. Перечислите основные программные модели Java.
    16. Метод является основной точкой входа для Java-приложений, которые выполняются под управлением операционной системы?
    17. Какая технология считается основным конкурентом Java-технологий?
    18. Охарактеризуйте спецификации JLS и JVMS.
    19. Охарактеризуйте понятие jar-архива.
    20. Какой Вы знаете инструмент для создания собственных пакетов и библиотек?

    ЧАСТЬ 2. НАПИСАНИЕ ПРОСТЕЙШИХ консольных приложений

    Раздел 2. 1. Основные средства для создания Java-приложений

        1. Общий обзор инструментов для написания Java-программ

          В первую очередь следует обратить внимание на JDK (Java Development Kit) — пакет утилит от Oracle; в основном это утилиты командной строки. Но, как правило, удобнее работать с интегрированными средами разработки (IDE), которые существенно облегчают выполнение многих этапов и позволяют в значительной степени автоматизировать процесс создания проекта в целом. На момент написания этого раздела наиболее популярными из них являются NetBeans и Eclipse, а также IDEA.

          Если сравнивать NetBeans и Eclipse, их возможности более или менее равнозначными (по крайней мере, на начальном этапе изучения, а также для создания простых приложений различия между этими пакетами не имеют особого значения). Можно сказать, что специалисты из Oracle в большей степени рекомендуют NetBeans. Профессиональные программисты больше любят Eclipse, чем NetBeans, но NetBeans часто рассматривается как пакет, более удобный для начального изучения.

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

          • средства тестирования, в первую очередь JUnit;
          • средства отладки;
          • средства профилирования программ, предназначенные в первую очередь для оценки времени выполнения и памяти, расходуется:
          • средства автоматизированной сборки проектов (Maven, Ant)
          • средства для групповой работы.
        2. Основные утилиты JDK

          Наиболее известными и используемыми утилитами JDK являются:

          • java — интерпретатор байт-кода
          • javac — компилятор, который на основе исходного текста создает байт-код;
          • jar — утилита для создания архивов;
          • javadoc — утилита для автоматизированного создания документации
          • javap — дизассемблер байт-кода, который позволяет проанализировать откомпилированное программу и посмотреть ее байт-код в мнемоническом виде, удобном для восприятия человеком.
        3. Компиляция и выполнение программ средствами JDK

          Напишем применения с минимальной функциональностью, которое иллюстрирует типичную структуру консольных Java-приложений. Программа, которая выводит на экран статический текст Hello, world!, Имеет вид:

          / * The simplest program * /

          public class HelloWorld {

          public static void main (String [] args) {

          System.out.println ( «Hello, world!»)

          }

          }

          Листинг 2.1.3.1. Простая программа — компиляция и выполнение

          Мы видим, что даже такая простая программа должна быть оформлена как класс (в данном случае HelloWorld). Текст этой программы должен быть сохранен в файле HelloWorld.java. Java требует, чтобы общедоступные классы (те, которые сопровождаются модификатором public) хранились в файлах с расширением .java, а название файла должно совпадать с именем класса.

          Здесь и далее мы будем ориентироваться на использование операционной системы

          Windows.

          Запустим сеанс командной строки и перейдем в каталог, в котором содержится файл с исходным кодом программы. Компиляция программы осуществляется командой

          javac HelloWorld.java

          В результате компиляции образуется байт-код, который выполняется под управлением виртуальной машины. Технически байт-код сохраняется в файле с расширением .class; для каждого класса исходного файла образуется отдельный файл с байт-кодом. В нашем случае будет создан файл HelloWorld.class.

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

          машину. наберем команду

          java HelloWorld

          (Без расширения).

          Мы увидим следующий результат:

          image

        4. Настройка переменных окружения

          Не всегда все идет так гладко — даже если сама программа написана без ошибок. Для нормальной работы в среде JDK необходимо настроить переменные окружения — соответствующие переменные операционной системы. Как минимум, речь идет о переменных PATH и CLASSPATH.

          Часто может случиться так, что система отказывается выполнять, например, компиляцию и сообщает о том, что не понимает команды javac. Это означает. что операционная система не может найти утилиту javac, поскольку не знает, в каком каталоге она содержится. В этом случае утилиту javac можно запустить на выполнение, если написать ее полное имя (собственное вместе с путем к программе). Но гораздо удобнее предварительно указать путь к нужных утилит в переменной окружения PATH; это делается стандартными

          средствами операционной системы.

          Для того, чтобы виртуальная машина могла найти нужные классы, каталоги, в которых они находятся, или же соответствующие jar-файлы должны быть указаны в переменной окружения CLASSPATH.

        5. Программа с несколькими классами Проведем небольшой эксперимент.

          Напишем простую программу, которая состоит из двух классов (сейчас нам достаточно знать лишь то, что один класс может использовать другой — например, если в нем будет вызван нужный метод этого класса).

          Исходный код имеет вид

          public class MainClass {

          public static void main (String [] args) {

          Informer.inform ();

          }

          }

          class Informer {

          public static void inform () {

          System.out.println ( «This is very important information»)

          }

          }

          Листинг 2.1.5.1. Программа с несколькими классами

          Здесь мы имеем два класса: класс Informer, метод inform () которого выводит на экран нужное сообщение, и класс MainClass, который вызывает этот метод. Для упрощения мы сделали метод inform () статическим — тогда не будет необходимости в создании экземпляров класса Informer; вызов метода осуществляется через имя класса (Informer.inform ()).

          Опять же, класс MainClass должен содержать метод main (). В каком файле нужно сохранить этот текст?

          Правильно — MainClass.java (по имени класса).

          Может возникнуть вопрос — а как же быть с классом Informer, ведь он тоже должен храниться в соответствующем файле? Нет, это не обязательно. Правило о соответствии имен файлов и классов касается только классов с модификатором public, а класс Informer не имеет такого модификатора и не является публичным.

          Конечно, мы можем разместить каждый класс в отдельном файле (в большинстве случаев именно так и нужно делать).

          -Прежнему запустим сеанс командной строки и перейдем в каталог, в котором хранится файл MainClass.java. Откомпилируем его так же, как и раньше:

          javac MainClass.java

          Несмотря на то, что класс Informer явным мере не компилировался, он все равно будет откомпилированный и будет создан соответствующий файл с байт-кодом. Таким образом, будет создано два файла с расширением .class (MainClass.class и Informer.class) — по количеству классов в файле. Мы можем в этом убедиться с помощью команды dir (еще раз нагадаэмо, что мы рассматриваем среду Windows, в Unix-подобных системах аналогом этой команды является ls). В результате мы увидим

          image

          Выполним метод main класса MainClass:

          java MainClass

          Как и следовало ожидать, мы увидим

          image

        6. опция -classpath

          Теперь проведем небольшой эксперимент. Создадим подкаталог sub и переместим в него (не скопируем) файл Informer.class.

          Попробуем теперь выполнить основную программу (MainClass):

          java MainClass

          Мы увидим сообщение об ошибке

          image

          Действительно, теперь файл Informer.class находится в другом каталоге, этот каталог не указан в переменной CLASSPATH, и поэтому виртуальная машина не может его найти.

          Для того, чтобы все-таки запустить программу один или несколько раз, не обязательно включать все необходимые каталоги в CLASSPATH. Можно указать их непосредственно при запуске интерпретатора с помощью опции -classpath (или сокращенно -cp):

          java -cp sub MainClass

          Неожиданно видим:

          image

          Как же так? Теперь интерпретатор не находит класса MainClass в текущем каталоге?

          Это характерный «подводный камень». Дело в том, что если используется опция -cp, то учитываются только те каталоги, которые указаны в этой опции, и поэтому поиск в текущем каталоге не производится. Выход есть: в опции -cp нужно указать и текущий каталог:

          java -cp.; sub MainClass

          Теперь все правильно:

          image

        7. Создание Java-приложения средствами Eclipse

          Как уже отмечалось, для профессионального программирования на Java используются IDE, подобные Eclipse или NetBeans. Для примера возьмем Eclipse и создадим с его помощью простое приложение, которое выводит на экран Hello, world.

          Начнем с создания проекта. Запустим Eclipse. Среда не должна иметь примерно такой вид:

          image

          Можно выбирать между различными перспективами, то есть настроек.

          Выберем пункт меню File => New => JavaProject. Должно появиться окно, похожее на следующее:

          image

          Даем проекта название (например, firstapp) выбираем нужное JRE. Можно сразу же подключить дополнительные jar-файлы и библиотеки, которые могут использоваться в проекте. Не будем этого делать и нажмем на Finish.

          После этого мы должны увидеть окно, подобное следующему:

          image

          Далее нужно создать пакет (это можно сделать с помощью контекстного меню, если щелкнуть правой кнопкой мыши на названии проекта). Назовем его так же, как и проект — first.

          Далее создаем в этом пакете новый класс. В соответствующей форме пишем название класса (пусть это будет FirstApp). Поскольку это главный класс приложения, в котором должен быть метод main, ставим соответствующую галочку:

          image

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

          Мы видим заготовку программы, созданную мастером Eclipse:

          image

          Набираем текст программы. При этом обратите внимание на подсказки Eclipse, направленные на облегчение и ускорение набора.

          В результате получим:

          image

          Запускаем программу на выполнение — или нажав мышью на зеленый треугольник, или с помощью пункта меню Run. На вкладке Console мы увидим результат работы программы:

          image

          File Edit Source Ilefactor Navigate Search Project Run Window I-JeIp

          image

        8. Создание Java-приложения средствами NetBeans

    Теперь создадим приложение, которое выводит на экран Hello, world, с помощью IDE NetBeans.

    Запустим NetBeans. Среда может иметь примерно такой вид:

    image

    Начнем создание нового проекта (меню Файл => Создать проект …). Откроется диалоговое окно:

    image

    В категории «Java» выберем тип проекта: «Приложение Java». Нажмем «Далее». Появится окно:

    image

    Здесь мы имеем указать название проекта (пусть это будет helloworld). NetBeans предложит сразу создать соответствующий основной класс. Можно было бы с этим согласиться, но предложенное название класса Helloworld не слишком соответствует стандартным договоренностям Java Code Conventions по имен классов. Поэтому изменим ее на HelloWorld и нажмем на кнопку «Готово».

    Видим заготовку, созданную мастером NetBeans:

    image

    Заметим, что в отличие от Eclipse, NetBeans сам создал и пакет, и класс (а заодно и метод main).

    Можем писать код; при этом не забываем о возможности использования клавиатурных комбинаций и подсказок от среды.

    В результате получим:

    image

    Запускаем программу (нажимаем на зеленый треугольник или используем меню). Получаем результаты:

    G’a fi / 1 N pa eva Bød, N epaop, t’1c <op, n bil Øa fi / 1 P eopran øsa u, øe xop, a Bbin o / 1 n ø + b OT / 1 ap, xa N poØø / 1 ø poea + b Fpynn a C epeø c Oxn o C n pa eva Q • + » ‘!»» * ‘+!!+!)

    BseoQ-EeMo • woNd (run) n

    15

    16 —

    pablic static hoid maiD (5tring [] args) (

    2.1.8 Знакомство с дизассемблеры

    Анализ байт-кода, который вследствие компиляции Java-приложения, часто помогает более глубоко разобраться в деталях ее работы. Как основной инструмент для получения мнемонического записи байт-кода (дизассемблирование) рассматривается утилита javap, которая входит в состав JDK.

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

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

    Код программы имеет вид

    public class MainClass {

    public static void main (String [] args) {int a = 10;

    int b = 20; int s = a + b;

    }

    }

    Листинг 2.1.8.1. Элементарная программа для дизассемблирование Сохраним этот текст в файле MainClass.java и Откомпилируем его: javac MainClass.java

    Дизасемблюемо байт-код:

    javap -c MainClass> bytecode

    В результате переадресации дизасембльований код будет сохранен в файле

    bytecode.Он может иметь вид:

    Compiled from «MainClass.java» public class MainClass {

    public MainClass (); Code:

    0: aload_0

    1: invokespecial # 1 // Method java / lang / Object «. <Init>» :() V

    4: return

    public static void main (java.lang.String []) Code:

    0: bipush 10

    2: istore_1

    3: bipush 20

    5: istore_2

    6: iload_1

    7: iload_2

    8: iadd

    9: istore_3

    10: return

    }

    Листинг 2.1.8.2. Результат дизассемблирование программы из листинга 2.1.8.1

    Мы видим, что метод main () превратился в последовательность команд Java-машины. Эти команды описаны в спецификации JVMS, и для того, чтобы разобраться в сути байт-кода, нужно обратиться к этой спецификации.

    Раздел 2.2. Анатомия простого применения

        1. основной класс

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

          public class MyClass {

          public static void main (String [] args) {System.out.println ( «Hello, world!»)

          }

          }

          Листинг 2.2.1.1. Основной класс консольного приложения должен быть сохранен в файле MyClass.java.

        2. Класс и файл: названия могут не совпадать

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

          Вполне можно написать, например, такой код:

          class MyClass {

          public static void main (String [] args) {System.out.println ( «Hello, world!»)

          }

          }

          Листинг 2.2.2.1. Основной класс является публичным

          сохранить его в файле с любым названием с расширением .java — например, manyvariants.java и откомпилировать обычным образом:

          javac manyvariants.java

          Но при этом все равно создастся файл MyClass.class, и запустить программу, как и следовало ожидать, надо так:

          java MyClass

          Таким образом, файл с исходным кодом может иметь вид:

          class K1 {

          // Какой код

          }

          class K2 {

          // Какой код

          }

          В начале файла может быть директива package, которая свидетельствует о включении классов к тому или иному пакета. Кроме того, могут быть директивы import, которые подключают другие пакеты.

        3. Метод main и его варианты

          Основным методом, точкой входа в программу является метод main (), который чаще всего имеет вид

          public static void main (String [] args)

          Именно этот метод вызывается виртуальной машиной и выполняется.

          В принципе, метод main () является обычным методом; в частности он может быть перегружен (то есть в классе может быть несколько методов main с разной сигнатурой). Но, несмотря на это, вызываться будет один из них — тот, который имеет приведенную выше сигнатуру. Если же виртуальная машина не найдет нужный метод возникнет ошибка времени выполнения — так называемое исключение.

          Метод main должен быть описан как public (общедоступный), поскольку в противном случае виртуальная машина не будет доступа к нему и, соответственно, не сможет его вызвать.

          Метод main должен быть описан как static (статический), поскольку этот метод вызывается тогда, когда не существует ни одного экземпляра класса first.

          Поскольку метод ничего не возвращает, он должен быть описан как void (обратите внимание это одна из «тихих», но важных отличий от C ++, стандарт которого требует, чтобы метод main () возвращал int.

          Модификаторы static и public можно переставить местами.

          Для описания параметров метода main () можно использовать форму функции с переменным количеством аргументов (эта возможность существует начиная с Java 5). То есть вместо

          public static void main (String [] params)

          можно написать

          public static void main (String … params)

          Рассмотрим небольшой контрольный вопрос.

          Дано несколько вариантов написания метода main. Сколько из них будут компилироваться:

          а) public static void main (String … a) б) public static void main (String. * a) в) public static void main (String … a)

          г) public static void main (String [] … a) д) public static void main (String … [] a)

          Ответ — 3; это варианты а), в), г). Здесь есть небольшая ловушка: вопросы не о том, сколько вариантов могут быть выполнены, а о том, сколько будут компилироваться. Вариант г) не является законным с точки зрения виртуальной машины, но нормально пропускается компилятором — этот метод принимает переменное количество массивов, элементами которых являются строки.

        4. Варианты Hello, world

    Вернемся к программе Hello, world, которая выводит на экран некоторый текст.

    public class HelloWorld {

    public static void main (String [] args) {

    System.out.println ( «Hello, world!»)

    }

    }

    Листинг 2.2.4.1. Самый простой вариант Hello, world: вывод ЛИТЕРАЛЬ Проиллюстрируем некоторые возможные варианты этой программы.

    Во-первых, в выводе фигурирует литерал — строковое значение Hello, world. Кроме простых случаев, такая практика не особенно рекомендованной, поскольку злоупотребление литерально константами в тексте программы снижает ее гибкость и повышает риск ошибок. Лучше предоставить значение строки некоторой переменной и в дальнейшем использовать эту переменную:

    public class HelloWorld {

    public static void main (String [] args) {String s = «Hello, world!»; System.out.println (s);

    }

    }

    Листинг 2.2.4.2. Вывод строчной переменной

    Еще лучше (поскольку строка, с которым мы имеем дело), ​​меняться не будет, объявить

    s как константу. Для этого используем ключевое слово final:

    public class HelloWorld {

    public static void main (String [] args) {final String s = «Hello, world!»; System.out.println (s);

    }

    }

    Листинг 2.2.2.3 .. вывод константы

    Можно вынести переменную s за пределы метода main и сделать ее полем класса.

    Но следующий вариант не проходит и приводит к ошибке компиляции:

    public class HelloWorld {

    final String s = «Hello, world!»;

    public static void main (String [] args) {System.out.println (s);

    }

    }

    Листинг 2.2.2.4. Вывод поля класса: неправильный вариант

    Дело в том, что метод main является статическим и может обращаться только к статическим полей класса. Нужно сделать поле s статическим:

    public class HelloWorld {

    final static String s = «Hello, world!»;

    public static void main (String [] args) {System.out.println (s);

    }

    }

    Листинг 2.2.4.5. Вывод статического поля: правильно 2.2.5.Аргументы командной строки

    Важное значение имеет параметр метода main ().Он является массивом строк (экземпляров

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

    Например напишем программу, которая определяет длины строк, переданных в качестве аргументов командной строки.

    Создадим файл StringLength.java с последующим содержанием:

    public class StringLength {

    public static void main (String [] params) {

    int paramcount = params.length; for (int i = 0; i <paramcount; i ++) {

    System.out.println ( «Length of» + params [i] + «is» + params [i] .length ());

    }

    }

    }

    Листинг 2.2.5.1. Использование аргументов командной строки

    Мы использовали «классическую» форму перебора элементов массива params (чтобы подчеркнуть, что это обычный массив, мы изменили его традиционное название). Посредством обращения

    int paramcount = params.length;

    мы получаем длину этого массива (содержательно — количество строк, которые нас интересуют). Далее перебираем элементы этого массива с помощью цикла for (нумерация элементов массива, так же как в языке Си, начинается с 0):

    for (int i = 0; i <paramcount; i ++) {

    System.out.println ( «Length of» + params [i] + «is» + params [i]. Length ());

    }

    Существует еще один способ перебора элементов массива — модифицированный цикл for, который появился в Java 5 (часто его называют циклом foreach, поскольку именно так подобный цикл называется в ряде других языков программирования). Подробнее о нем будет идти дальше.

    Откомпилируем эту программу обычным образом:

    javac StringLength.java

    При запуске следует передать программе какие-то данные. Например, если нас интересуют длины строк aaa, qwerty и hello world, программу следует запустить следующим образом:

    java StringLength aaa qwerty «hello world»

    Вывод будет иметь вид:

    image

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

    Такая модификация могла бы иметь вид:

    public class StringLength {

    public static void main (String [] params) {

    int paramcount = params.length;

    if (paramcount == 0) {

    System.out.println ( «Usage: java stringlength str1 …»);

    System.exit (0);

    }

    for (int i = 0; i <paramcount; i ++) {

    System.out.println ( «Length of» + params [i] + «is» + params [i] .length ());

    }

    }

    }

    Листинг 2.2.5.2. Проверка правильности передачи аргументов командной строки Вызов System.exit (0), то есть вызов метода exit класса System завершает

    работу программы. Вместо этого в данном случае можно было бы использовать инструкцию

    return.

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

        1. Программа без метода main

          Можно написать консольное приложение, которое нормально работает, но не использует метода main? Речь идет не о возможности компиляции (мы уже отмечали, что компилятор не требует его наличии, для него это такой же метод, как и любой другой), а именно о выполнении. Такая возможность никогда не была заказной, но она существовала до Java 6 включительно.

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

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

          public class WithoutMain {

          static {

          System.out.println ( «Main is deprecated!») System.exit (0);

          }

          }

          Листинг 2.2.6.1. Программа без метода main

          Эта программа нормально компилювалася, выводила сообщение

          Main is deprecated!

          и прекращала работу через вызов System.exit (0).

          Так к Java 6 включительно. Но в Java 7 возникает ошибка времени выполнения — виртуальная машина сообщает об отсутствии метода main:

          image

        2. Понятие о пакетах

    В первом приближении пакеты — это определенным образом организованные группы классов. Каждый класс может быть включен в определенного пакета. Например, для включения класса Kl в пакет pak в начале файла с исходным кодом должна содержаться инструкция package:

    package pak; class Kl {

    }

    Имя класса состоит собственно из имени класса и имени пакета. Так, в нашем примере полное имя класса Kl. который содержится в пакете pak, будет pak.Kl.

    Имя пакета может быть и более длинным. Например, класс String содержится в пакете java.lang, и поэтому его полное имя — java.lang.String.

    Обратите внимание — класс идентифицируется полным именем! Так, говорить о свойствах класса String или любого другого класса не имеет смысла, пока не будет однозначно определено, к какому именно пакета он принадлежит. Когда речь идет о классе String, практически всегда имеется в виду «стандартный» java.lang.String — но это совсем не обязательно. Вполне возможно создать собственный пакет, в котором будет свой класс String.

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

    import pak.Kl;

    Можно импортировать все классы пакета: import pak.*;

    В пакете java.lang собраны наиболее важные классы, и он импортируется автоматически.

    Раздел 2.3. Операции ввода-вывода

        1. Общее понятие о ввода-вывода

          Программа должна иметь возможность ввести данные, с которыми она работает, с внешнего источника (в простейшем случае — с клавиатуры) и результаты своей работы (в простейшем случае — на экран). Такие средства есть и в языке Java.

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

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

        2. Стандартные потоки ввода-вывода.

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

          java.lang.System):

          • System.in — стандартный ввод;

          • System.out — стандартный вывод;

          • System.err — стандартная ошибка; в этот поток направляются сообщения об ошибках, которые возникают во время выполнения программы.

          Поток System.out предоставляет удобные средства для вывода данных различных типов (мы видели это в предыдущих главах). Но возможности потока System.in для ввода данных значительно беднее — фактически его можно использовать разве что для введения отдельных символов.

        3. ввод символов

          Попробуем ввести символ с клавиатуры:

          package demobasicio;

          import java.io.IOException; public class DemoBasicIO {

          public static void main (String [] args) {System.out.println ( «Enter a symbol») int c = System.in.read ();

          System.out.println ( «You entered symbol» + c)

          }

          }

          Листинг 2.3.3.1. Ввод символа: характерная ошибка компиляции

          В чем дело? Почему компилятор выдает сообщение о синтаксической ошибке?

          Считается, что при выполнении операций ввода-то может пойти не так, и может возникнуть ошибочное ситуация — исключение; в данном случае — исключение IOException. Это исключение является контролируемым: компилятор следит за возможностью его

          возникновения. Необходимо либо перехватить и обработать это исключение с помощью инструкции try-catch-finally (см.далее), или явно сообщить компилятору о том, что метод не обрабатывает это исключение, а выбрасывает его наружу, с помощью ключевого слова throws, которое добавляется к описанию метода:

          package demobasicio;

          import java.io.IOException; public class DemoBasicIO {

          public static void main (String [] args) throws IOException {System.out.println ( «Enter a symbol»)

          int c = System.in.read ();

          System.out.println ( «You entered symbol» + c)

          }

          }

          Листинг 2.3.3.2. Ввод символа и не совсем правильное его вывода

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

          Если мы введем символ A, на экран выводится число 65.

          Дело в том, что символы хранятся в памяти компьютера как их коды, то есть как целые значения, и результатом метода System.in.read () является целое число. Поэтому переменная c (результат введения) имеет целый тип int, и при ее выводе мы видим не сам символ, а его код. Для того, чтобы все-таки был выведен символ, нужно осуществить приведение к символьного типа char:

          package demobasicio;

          import java.io.IOException; public class DemoBasicIO {

          public static void main (String [] args) throws IOException {System.out.println ( «Enter a symbol»)

          int c = System.in.read ();

          System.out.println ( «You entered symbol» + (char) c)

          }

          }

          Листинг 2.3.3.3. Ввода и вывода символа: исправленный вариант Теперь при вводе символа ‘A’ на экран выводится именно этот символ.

          Но поток System.in не предоставляет удобных средств ни для ввода строк, ни для

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

        4. Классическая схема ввода строк

          К Java 5 наиболее применяемый способ ввода строк, а также данных других типов заключался в использовании потока BufferedReader из пакета java.io. Общую схему введения на основе этого класса можно охарактеризовать следующим образом.

          Сначала создается поток ввода как экземпляр класса BufferedReader:

          BufferedReader br = new BufferedReader (new InputStreamReader (System.in))

          Далее с помощью метода readLine можно считать очередной строку (введение заканчивается, когда пользователь нажимает на Enter:

          String St = br.readLine ();

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

          import java.io.*;

          public class UsingBufferedReader {

          public static void main (String [] args) throws Exception {BufferedReader br = new BufferedReader (new

          InputStreamReader (System.in))

          System.out.println ( «Enter your name»); String name = br.readLine (); System.out.println ( «Hello,» + name)

          }

          }

          Листинг 2.3.4.1. Введение строк с помощью класса BufferedReader

        5. Использование конкатенации для вывода

          Обратите внимание на то, как в листинге 2.3.4.1 формируется поздравления, выводимого на экран:

          System.out.println ( «Hello,» + name)

          Метод println () принимает только один аргумент, который должен быть строкой. Поэтому для формирования нужного вывода очень типичным было и остается использование конкатенации (сцепления) строк (оператор +). До появления форматированного вывода (см п.2.3.8) этот способ формирования вывода был фактически основным.

          В данном случае для формирования поздравления осуществляется конкатенация двух строк: неизменной константы «Hello,» и переменной name, которая вводится во время работы программы.

        6. Ввод данных других типов

          Метод readLine () класса java.io.BufferedReader позволяет вводить только строки. Поэтому в случае, если нужно ввести данные других типов (например, числа), можно применить следующую схему: ввести данные как строку, а затем осуществить соответствующее преобразование.

          Рассмотрим простой пример: программу, которая вводит с клавиатуры целое число и выводит на экран его квадрат. Основная часть программы может иметь вид

          BufferedReader br = new BufferedReader (new InputStreamReader (System.in))

          System.out.println ( «Enter the integer value») String strNumber = br.readLine ();

          int number = Integer.parseInt (strNumber)

          System.out.println ( «The square of» + number + «is» + number * number)

          Листинг 2.3.6.1. ввод чисел

          Как и раньше, здесь создается поток br класса BufferedReader; вызов

          String strNumber = br.readLine ();

          обеспечивает ввод нужного числа как строки.

          Преобразование строки в целое число осуществляется с помощью вызова

          int number = Integer.parseInt (strNumber)

          Здесь используется метод parseInt класса Integer — так называемой объектной оболочки для целых чисел. Аналогичным образом можно вводить данные других простых типов.

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

        7. Бидьш новые возможности для ввода и вывода

          Начиная с Java 5, появились более удобные средства ввода-вывода.

          Во-первых, появился класс Scanner из пакета java.util, методы которого обеспечивают возможность ввода данных различных типов: строк, чисел и др.

          Во-вторых, появился метод printf (), хорошо знакомый программистам на С / С ++. Он позволяет отформатировать то, что выводится за счет задания в форматном строке спецификаторов формата.

        8. форматированный вывод

    Приведем пример использования функции printf для форматирования вывода.

    В результате выполнения фрагмента кода

    double r = 10./ 3.; System.out.println ( «Result is» + r)

    будет выведено

    Result is 3.3333333333333335

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

    Код может выглядеть

    double r = 10./ 3.;

    System.out.printf ( «Result is% 4.2f \ n», r)

    он выведет

    Result is 3,33

    Здесь вывода результата отводится 4 позиции; на дробную часть — 2 позиции. 2.3.9. Использование класса Scanner

    В Java 5 появился класс java.util.Scanner, который предоставляет удобные средства для ввода

    данных. Сначала создается экземпляр этого класса:

    Scanner sc = new Scanner (System.in)

    после чего можно использовать методы этого класса для ввода данных различных типов. Приведем несколько примеров.

    Попробуем ввести имя пользователя как строку с помощью метода nextLine () и вывести поздравления этом пользователю:

    package demoscanner; import java.util.Scanner; public class DemoScanner {

    public static void main (String [] args) {

    Scanner sc = new Scanner (System.in) System.out.println ( «Enter your name»); String st = sc.nextLine (); System.out.println ( «Hello,» + st)

    }

    }

    Листинг 2.3.9.1. Класс Scanner: введение строки

    Введем с клавиатуры целое число с помощью функции nextInt () и выведем на экран квадрат этого числа:

    public static void main (String [] args) {

    Scanner sc = new Scanner (System.in) System.out.println ( «Enter the number») int n = sc.nextInt ();

    System.out.println ( «The square of» + n + «is» + n * n)

    }

    Листинг 2.1.9.2. Класс Scanner: введение целого числа

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

    Попробуем ввести строку, которая состоит из нескольких слов, и разделить его на отдельные слова. Воспользуемся тем, что метод next () считывает следующее слово (хотя следует отметить, что с целью расщепления строк частише используются другие средства). Сначала рассмотрим более простой случай, когда мы заранее знаем количество слов в строке (например, 3):

    public static void main (String [] args) {

    Scanner sc = new Scanner (System.in) System.out.println ( «Enter the string») System.out.println (sc.next ()); System.out.println (sc.next ()); System.out.println (sc.next ());

    }

    Листинг 2.3.9.3. Использование Scanner для расщепления строки; количество слов известна заранее

    Если ввести предложения Life is wonderful, программа выведет

    Life is

    wonderful

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

    циклы, например:

    public static void main (String [] args) {

    Scanner sc = new Scanner (System.in) System.out.println ( «Enter the string») while (sc.hasNext ()) {

    System.out.println (sc.next ());

    }

    Листинг 2.3.9.4. Использование Scanner для расщепления строки; количество слов неизвестна

    Вводим предложения To be or not to be; программа выводит

    To be or not to be

    2.3.10. Некоторые особенности и сюрпризы класса Scanner

    Применение класса Scanner связано с рядом особенностей и тонкостей.

    Проиллюстрируем некоторые из них.

    Рассмотрим программу

    package scannersurprise; import java.util.Scanner; public class ScannerSurprise {

    public static void main (String [] args) {Scanner scan = new Scanner (System.in)

    System.out.println ( «Enter your number») int n = scan.nextInt (); System.out.println ( «Enter first string») String first = scan.nextLine ();System.out.println ( «Enter second string») String second = scan.nextLine ();

    System.out.printf ( «Number was% d; strings were:% s and

    % S \ n «, n, first, second)

    }

    }

    Листинг 2.3.10.1. Неправильное введение целых чисел и строк

    Эта программа работает неправильно: перескакивает через введение первой строки. Диалог с пользователем может иметь вид:

    Enter your number 5

    Enter first string Enter second string qwerty

    Number was 5, strings were: and qwerty Можно попробовать после ввода числа добавить операцию scan.skip ( «\ n»);

    (Пропуск определенного текста, в данном случае — символа новой строки), но и эта операция тоже не является хорошей. В ней жестко прописан разделитель строк, и поэтому работа программы становится зависимой от среды.

    Лучше получить разделитель из системных свойств:

    String ls = System.getProperty ( «line.separator»)

    scan.skip (ls)

    Но даже в этом случае программа работает по-разному в различных средах, в частности в NetBeans и Eclipse.

    Для этой задачи можно попробовать простое решение: после считывания числа просто дополнительно считать следующий (пустой) строка:

    Scanner scan = new Scanner (System.in)

    System.out.println ( «Enter your number») int n = scan.nextInt ();

    scan.nextLine ();

    System.out.println ( «Enter first string») String first = scan.nextLine (); System.out.println ( «Enter second string») String second = scan.nextLine ();

    System.out.printf ( «Number was% d; strings were:% s and

    % S \ n «, n, first, second)

    Листинг 2.3.10.1. Введение целых чисел и строк: исправленный вариант 2.3.11.Использование диалоговых окон для ввода ввода

    Иногда для ввода применяются элементы графического интерфейса. Для этого используются стандартные диалоговые окна, предоставляемых классом JOptionPane из пакета javax.swing. Такая программа может иметь вид:

    package inputdialog;

    import javax.swing.JOptionPane; public class InputDialog {

    public static void main (String [] args) {

    String inp = JOptionPane.showInputDialog (null,

    «Enter your name», «Our question»,

    JOptionPane.QUESTION_MESSAGE)

    System.out.println ( «Hello,» + inp + «!»)

    }

    }

    Листинг 2.3.11.1. Введение с помощью диалогового окна

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

    image

    2.3.12. класс Console

    В Java 6 появился класс Console. Несмотря на многообещающее название, этот класс не является таким уж мощным: он методы только для ввода с клавиатуры строк и паролей. Считается, что основным назначением этого класса было обеспечение возможности для ввода паролей в зашифрованном виде.

    Например, программа, которая выводит на экран введенные имя пользователя и пароль, может иметь вид:

    import java.io.Console; public class UseConsole {

    public static void main (String [] args) {final String LOGIN_PROMPT = «Enter login:»;

    final String PASSWORD_PROMPT = «Enter password:»; Console console = System.console ();

    String login = console.readLine (LOGIN_PROMPT)

    char [] password = console.readPassword (PASSWORD_PROMPT) System.out.println ( «Login is» + login) System.out.println ( «Password is» + new String (password))

    }

    }

    Листинг 2.3.12.1. Класс Console: введение имен и паролей

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

    КОНТРОЛЬНЫЕ ВОПРОСЫ

    1. Охарактеризуйте известные Вам инструментальные средства для создания Java-программ.
    2. Опишите назначение основных утилит JDK (java, javac, jar, javap, javadoc).
    3. Как средствами JDK откомпилировать программу и запустить ее на выполнение?
    4. Охарактеризуйте призначеня переменных окружения PATH и CLASSPATH.
    5. Как можно обеспечить выполнение программы, если ее класса нет в текущем каталоге и он не записан в CLASSPATH?
    6. Охарактеризуйте основные шаги для создания Java-приложения в среде Eclipse.
    7. Каким образом можно проверить байт-код класса?
    8. Опишите основное правило относительно названий публичных классов.
    9. Обязательно название класса должна совпадать с названием файла, в котором содержится его исходный код?
    10. С вызова какого метода начинается выполнение обычного консольного приложения?
    11. Может в классе быть несколько методов main?
    12. Можно написать класс, в котором нет метода main?
    13. Можно написать консольное приложение — класс, в котором нет метода main, но применение нормально компилируется и выполняется?
    14. Перечислите известные Вам варианты написания метода main.
    15. Что такое аргументы командной строки?
    16. Каким образом можно задать аргументы командной строки в среде Eclipse?
    17. Охарактеризуйте понятие пакета.
    18. Что такое полное имя класса?
    19. Назовите полное имя стандартного класса String (строка).
    20. Пакет импортируется автоматически?
    21. Перечислите стандартные потоки ввода-вывода в Java
    22. Каким образом можно вывести на экран определенную информацию?
    23. Охарактеризуйте известные Вам способы ввода строк и данных других типов.
    24. Как преобразовать строку в число?
    25. Каким образом можно отформатировать вывод?
    26. Охарактеризуйте основные возможности класса Scanner.
    27. Каким образом для ввода информации можно использовать диалоговые окна?
    28. Охарактеризуйте класс Console. Какой возможности класса Console нет в классе java.io.BufferedReader?

    Часть 3. ОБЗОР ОСНОВНЫХ ВОЗМОЖНОСТЕЙ ЯЗЫКА

    Введение

    Java является объектно-ориентированным языком. Непосредственно поддерживается возможность описания классов, которые отвечают определенным понятием; создание конкретных экземпляров классов; взаимодействия между ними и тому подобное. Но объектно-ориентированная парадигма существенно опирается на методики процедурного или императивного программирования. Органично используются условные операции, циклы и т. Методы классов — это по сути функции, включенные в состав классов.

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

    Аналогично, процедурная по своей сути программа может обращаться на уровне «черного ящика» с функциями, которые предоставляются существующими классами. Например, для вывода текущей даты можно написать код

    package showdate; import java.util.Date; public class ShowDate {

    public static void main (String [] args) {

    System.out.println ( «Current time is» + new Date ());

    }

    }

    Листинг 3.В.1. Пример использования классов — вывода текущей даты

    Здесь используются классы, но для понимания этого кода не обязательно иметь глубокие знания в области объектно-ориентированного программирования. Все, что нужно знать — это то, что нужно создать экземпляр класса Date из пакета java.util (при этом импортировать сам пакет) и вывести его на экран.

    Аналогично, для того, чтобы вывести на экран длину некоторого строки, например:

    String s = «qwerty»;

    int len ​​= s.length ();

    System.out.println ( «Length of the string» + s + «is» + len)

    достаточно знать, что строка является экземпляром класса String, и для получения длины строки надо на этом экземпляре вызвать метод length () этого класса.

    Язык Java имеет довольно стандартный набор процедурных рис (условные операции, циклы различных типов, переключатели и т.п.), которые в основном совпадают с процедурными характеристиками языка С ++. Что касается объектных рис, различия Java от С ++ намного более существенными — и по синтаксису, и по семантике.

    В этой части мы на довольно интуитивном уровне сделаем начальный обзор основных процедурных и объектных рис Java для понимания программных конструкций, которые будут использоваться или упоминаться далее. Мы сделали определенную попытку и в направлении того, чтобы

    раздел мог читаться людьми, для которых Java — вообще первый язык программирования (хотя для начального ознакомления с фундаментальными концепциями, как: общие требования к программам; алгоритмы, лежащие в основе программ; программы и данные и т.д. этого, конечно, недостаточно).

    Раздел 3.1. Обзор процедурных возможностей языка

    3.1.1. Комментарии

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

    Приведем программу с двумя комментариями, один из которых размещается в одной строке, а другой — в нескольких строках

    package usecomments; public class UseComments {

    public static void main (String [] args) {

    / *

    Эта программа выводит информацию о комментарии. Данный комментарий размещается в нескольких строках

    * /

    System.out.println ( «Use comments»); // Однострочный комментарий

    }

    }

    Листинг 3.1.1.1. Использование комментариев 3.1.2.Переменные и выражения

    Использование переменных в Java в своей основе мало чем отличается от таких языков, как

    С / С ++ или Паскаль. Интуитивно переменные — это величины, с которыми работает программа. Например, мы хотим написать программу, которая считает площадь прямоугольника S по формуле S = ab, где a и b — длины сторон. Подобный код пишется почти автоматически. Нужно описать три переменные: длины сторон a и b и площадь s. Все три переменные являются действительными и соответственно имеют тип double.

    Именно вычисления площади могло бы выглядеть следующим образом (здесь мы абстрагируемся от того, что конкретные значения лучше не задавать прямо в программе, а вводить как внешние данные):

    public class DemoVariables {

    public static void main (String [] args) {double s1 = 4.0;

    double s2 = 2.5; double square = s1 * s2;

    System.out.println ( «The square of rectangle with sides» + s1 + «and» + s2 + «is» + square)

    }

    }

    Листинг 3.1.2.1. Использование переменных — вычисление площади прямоугольника по его сторонами

    Единицы измерения длины здесь не имеют существенного значения; пусть это будут сантиметра. Тогда в данном случае длина стороны a равен 4 см; длина стороны b

    — 2.5 см, а результат s вычисляется по соответствующей формуле. Запись s1 * s2 — это простой пример выражения.

    С технической же точки зрения переменная — это определенный участок памяти с определенной адресу, на программном уровне характеризуется своим именем и куда записывается то или иное значение.

        1. Важнейшие типы данных

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

          Приведем пример того, как интерпретация переменной зависит от того, какой она

          тип.

          public class DemoVariables {

          public static void main (String [] args) {char c = ‘A’;

          System.out.println (c) // Выводится символ ‘A’ System.out.println ((int) c) // Выводится 65 — код символа

          }

          }

          Листинг 3.1.3.1. Вывод символа и его кода

          Здесь переменная c имеет символьный тип char и поэтому интерпретируется как символ. Но результат приведения (int) c уже имеет тип int, и поэтому инструкция

          System.out.println ((int) c)

          выводит код символа.

          Все типы в Java делятся на примитивные (другое название — простые) и объектные. К примитивных типов принадлежит четыре целочисленные типы (они отличаются размером; наиболее употребительным среди них является тип int) два действительных (наиболее применяемый — double) символьный тип — char. Мы уже видели примеры использования этих типов.

          Есть еще один простой тип — boolean; он предназначен для операций с логическими величинами. Переменные этого типа могут принимать одного из двух значений — false (недостаток) и true (истина).

          Проиллюстрируем этот тип на следующем примере.

          public static void main (String [] args) {boolean b = (2 + 2 == 5); System.out.println (b)

          }

          Листинг 3.1.3.2. Пример булевых переменных

          Здесь переменная b равен результату проверки того, равно выражение 2 + 2 значению

          1. Как можно ожидать, программа выводит false.

            Программист имеет возможность описывать собственные типы — прежде всего класса. Существует большое количество готовых классов. Сейчас отметим два из них:

            • класс Object — вершина иерархии для всех объектных типов;
            • класс String для задания строк.
        2. константы

          В программах часто бывает нужно объявлять константы. Для этого используется ключевое слово final, например

          final double PI = 3.14159;

        3. предоставление значения

          Операция = является операцией присваивания значения (или присваивания). Она выполняется следующим образом: вычисляется значение выражения, записанный справа от знака =, после чего это значение придается переменной, которая стоит слева. Так, после выполнения действий

          int a = 2; int b = 5,

          double s = a * b;

          переменные a, b, s приобретут значений 2, 5, 10.

          Не надо путать операции = и == — это разные операции. Пусть у нас есть две переменные типа int (целочисленный тип):

          int m = 2; int n = 3.

          Тогда операция n = m — это предоставление значения; после нее переменная n примет значение 2; переменная m не изменится. А выражение n == m — это операция проверки на равенство, результатом которого будет логическое значение false (ложь) сами значения переменных не изменятся.

        4. Основные операции Java

          Как и в других языках программирования, в выражениях Java можно применять различные операции (или операторы). Полный анализ операторов Java и их свойств заслуживает отдельного разговора. На интуитивном же уровне большинство операторов не требуют особых комментариев. Таковы, в частности, арифметические операторы: + (сложение), — (вычитание), * (умножение), / (деление — обычное для действительных чисел и целочисленное для целых),% (деление по модулю) логичны:! (Отрицание), && (конъюнкция), || (Дизъюнкция) отношение == (равно),!= (Не равно) другие сравнения (>, <, <=,> =) ряд других операторов.

          В разделах 3.1.8 и 3.1.9 мы остановимся на некоторых менее тривиальных операциях, будут часто использоваться в этом разделе.

        5. Оператор или инструкция?

          Следует обратить внимание на небольшую терминологическую тонкость.

          Для языка Паскаль были привычными термины «условный оператор», «оператор цикла и т.п.». В языках С / С была принята несколько иная терминология. Оператор (operator) в этих языках — это паскаливська операция, например +, -, / т. Именно подобные операторы прежде всего фигурируют в арифметических, логических и других выражениях. Паскаливськи же операторы, то есть программные же конструкции if, for и др. — это инструкции (statements) языков С / С ++.

          В этом плане Java тяготеет к терминологии С / С ++, и мы будем ориентироваться именно на нее. С другой стороны, формулировка «оператор цикла», «условный оператор» и т.п. являются общеупотребительными, и мы тоже будем их использовать.

        6. составленные присваивания

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

    c = c + 5;

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

    c + = 5;

    Подобные формы составленных присваиваний существуют и для многих других операторов. 3.1.9. Инкременты и декременты

    Для увеличения или уменьшения переменной на 1 существуют операторы инкремента и

    декремента, а именно:

    С ++постинкремент; значение переменной с используется, а затем увеличивается

    на 1;

    на 1;

    с—постдекремент; значение переменной с используется, а затем уменьшается

    ++ спреинкремент; значение переменной с увеличивается на 1, а затем

    используется;

    —спредекрементом; значение переменной c уменьшается на 1, а затем используется.

    Рассмотрим примеры, иллюстрирующие разницу между постинкрементом и преинкрементом; ситуация с декремент аналогична.

    В результате выполнения фрагмента кода

    int c = 5,

    System.out.println (c ++)

    будет выведено 5, поскольку значение переменной c увеличивается уже после вывода. В результате же выполнения фрагмента кода

    int c = 5,

    System.out.println (++ c)

    будет выведено 6.

    3.1.10. условные операторы

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

    Одна из возможных форм условного оператора выглядит так:

    if (условное выражение) операция_1; else операция_2;

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

    операция_1; в противном случае — операция_2.

    В качестве примера рассмотрим код, который находит наибольшее из двух чисел. Он может иметь вид (как и ранее, мы для простоты задаем значения переменных непосредственно в программе):

    public static void main (String [] args) {int a = 5;

    int b = 6; int maximum; if (a> b)

    maximum = a;

    else

    maximum = b;

    System.out.println ( «Maximum is» + maximum)

    }

    Листинг 3.1.10.1. Нахождение максимума двух чисел с помощью условного оператора

    Следует отметить, что при всей простоте этого кода данная задача может быть решена еще более просто. Часто условный оператор может быть заменен турнирных операцией (см. П.3.1.11). Что же касается собственно максимумов или минимумов двух цифр, то для их нахождения можно воспользоваться функцией max из класса Math, например

    int a = 5;

    int b = 6;

    int maximum = Math.max (a, b) System.out.println ( «Maximum is» + maximum)

    Листинг 3.1.10.2. Нахождение максимума двух чисел с помощью готовых функций

    3.1.10. тернарная операция

    Тернарная операция часто используется для упрощения вычисления переменных, которые зависят от определенного условия. В простейшем случае эта операция выглядит условие?значение_1: значение_2.Если условие истинно, значением этого выражения является значение_1, если ошибочной — значение_2.

    Перепишем код для нахождения максимума двух чисел (листинг 3.1.10.1) с использованием тернарной операции:

    public static void main (String [] args) {int a = 5;

    int b = 6;

    int maximum = (a> b)?a: b;

    System.out.println ( «Maximum is» + maximum)

    }

    Листинг 3.1.11.1. Нахождение максимума двух чисел с использованием тернарной операции

        1. циклы

          Цикл — это повторение некоторой операции фиксированное количество раз или пока выполняется определенное условие.Каждое отдельное повторение операции называется итерацией цикла.

          С самого начала Java имела три традиционные циклы С / С ++: цикл с предусловием, цикл с постусловием и цикл for. Начиная с Java 5, появился еще один цикл: модифицированный for, или, как его часто называют по аналогии с другими языками программирования, цикл foreach.

          На протяжении этой книги встречается значительное количество разнообразных циклов. Поэтому здесь будет приведен чисто иллюстративный пример решения одной и той же задачи на основе всех типов циклов (кроме модифицированного for, который будет проиллюстрировано на несколько другом примере).

          Задача «Сумма элементов массива» заключается в нахождении суммы элементов массива целых чисел. Массивы будут рассматриваться несколько позже, но сейчас можно сказать, что массив — это некоторая последовательность однотипных элементов.

          Сам массив будем задавать в программе следующим образом:

          int [] m = {перечень элементов};

          Например:

          int [] m = {5, 2, 10, 3, 40};

          Доступ к элементам осуществляется по его индексу (номером) при этом не забываем, что нумерация элементов (так же, как и в C ++) начинается с 0. Так, чтобы вывести на экран первый элемент массива, мы можем написать

          System.out.println (m [0]);

          Нам понадобится длина (количество элементов) массива; ее можно получить с помощью выражения m.length. Обозначим длину массива из n. Для простоты будем считать, что массив гарантированно не пустой, и в нем есть хотя бы несколько элементов.

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

          Цикл с предусловием (цикл while) имеет вид

          while (условие) операция;

          Это означает следующее: тело цикла (операция) должен выполняться, пока условие истинно. Если условие является ошибочной с самого начала, цикл не будет выполнен ни разу. Как применить этот цикл до нашей задачи? Введем вспомогательную переменную k, которую будем увеличивать на каждой итерации цикла; на каждой итерации к переменной s будет добавляться

          элемент m [k].Цикл будет выполняться пока k будет меньше n.

          Итак, решение нашей задачи с помощью цикла с предусловием может иметь вид:

          public class SumOfElements {

          public static void main (String [] args) {

          int [] m = {5, 2, 10, 3, 40};

          int n = m.length; int s = 0;

          int k = 0;

          while (k <n) {

          s + = m [k]; k ++;

          }

          System.out.println ( «Sum of elements is» + s);

          }

          }

          Листинг 3.1.12.1. Нахождение суммы элементов массива с помощью цикла с предусловием

          Что будет, если в теле цикла не написать операцию k ++? Это типичная ошибка, которую часто делают по невнимательности. В этом случае k меняться не будет, и поэтому условие k <n всегда оставаться истинной. Возникнет бесконечный цикл, который никогда не закончится (если программу не остановить принудительно). Говорят, что программа зацикливается.

          Цикл с постусловием (или цикл do-while) имеет вид

          do операция while условие;

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

          Решение нашей задачи на основе цикла с постусловием может иметь вид

          public class SumOfElements {

          public static void main (String [] args) {int [] m = {5, 2, 10, 3, 40};

          int n = m.length; int s = 0;

          int k = 0;

          do {s + = m [k]; k ++;

          }

          while (k <n)

          System.out.println ( «Sum of elements is» + s);

          }

          }

          Листинг 3.1.12.2. Нахождение суммы элементов массива с помощью цикла с постусловием

          Цикл for. Часто говорят, что цикл for лучше использовать в ситуации, когда количество повторений цикла является фиксированной. Возможно, эта мысль в определенной степени навеяна аналогиями с циклом for в Алголь или Паскале.

          Для задачи «Сумма элементов массива» цикл for действительно подходит идеально. Код на основе этого цикла, скорее всего, будет иметь вид

          public class SumOfElements {

          public static void main (String [] args) {int [] m = {5, 2, 10, 3, 40};

          int n = m.length; int s = 0;

          for (int i = 0; i <n; i ++) {s + = m [i];

          }

          System.out.println ( «Sum of elements is» + s);

          }

          }

          Листинг 3.1.12.3. Нахождение суммы элементов массива с помощью цикла for. Переменную k в этом контексте можно охарактеризовать как счетчик цикла.

          Обратите внимание: в отличие от предыдущих циклов, переменная k в теле цикла не

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

          Модифицированный for. Мы говорили, что цикл for идеально подходит для работы с массивами. Но модифицированный for (часто по аналогии с другими языками программирования его еще называют циклом foreach) для перебора массивов еще более удобным — именно для этого он и создавался.

          Этот цикл появился в Java 5. Он предназначен для удобного просмотра наборов данных: массивов, коллекций и тому подобное.

          Код для решения задачи «Сумма элементов массива», скорее всего, будет иметь вид, подобный следующему:

          public class SumOfElements {

          public static void main (String [] args) {int [] m = {5, 2, 10, 3, 40};

          int s = 0;

          for (int i: m) {s + = i;

          }

          System.out.println ( «Sum of elements is» + s);

          }

          }

          Листинг 3.1.12.4. Нахождение суммы элементов масивку с помощью модифицированного for

          Обратите внимание на следующее. Для решения нашей задачи с помощью модифицированного for знать длину массива не требуется. Счетчик цикла не используется; переменная i в приведенном цикле — это не индекс очередного элемента массива, а сам этот элемент. Попытка записать модифицированный for по аналогии с обычным циклом for:

          public class SumOfElements {

          public static void main (String [] args) {int [] m = {5, 2, 10, 3, 40};

          int s = 0;

          for (int i: m) {

          s + = m [i];

          }

          System.out.println ( «Sum of elements is» + s);

          }

          }

          Листинг 3.1.12.5. Модифицированный for — характерная ошибка

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

          Для выхода из цикла типичным является использование инструкции break, а для перехода к следующей итерации — инструкции continue.

        2. Циклы: сначала думаем, потом кодируем

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

          Рассмотрим задачу «Произведение дробей». Нужно написать функцию, которая вычисляет произведение

          n i

          i 1

          i 1

          f (n)

          i 1

          число n> 0 должно быть параметром функции. Для простоты мы игнорируем обработку ошибочных данных (например, ситуации, когда n <1).

          «Лобовое» решение, которое часто пишут в результате бездумного механического кодирования

          static double prodDrob (int n) {double prod = 1.;

          for (int i = 1; i <= n; i ++) {prod * = i / (i + 1);

          }

          return prod;

          }

          Листинг 3.1.13.1. Задача «Произведение дробей»: неправильное решение

          конечно же, не работает. Для любого аргумента n> 1 эта функция возвращает 0. Дело здесь в том, что переменная i имеет тип int. Поэтому выражение i / (i + 1) исчисляется по правилам целочисленного деления и для любого значения i равно 0.

          Попробуем исправить эту ошибку на основе использования double:

          static double prodDrob (int n) {double prod = 1.;

          for (int i = 1; i <= n; i ++) {prod * = i / (i + 1.);

          }

          return prod;

          }

          Листинг 3.1.13.2. Задача «Произведение дробей»: решение формально правильное, но неэффективное

          Теперь функция выдает верные результаты (по крайней мере для небольших n), но этот вариант все равно неудовлетворительный. Вопрос здесь в эффективности: для вычисления f (n) цикл вообще не нужен.Числительные и знаменатели взаимно сокращаются, и

          f (n) = (1/2) * (2/3) * … * (n / (n + 1)) = 1 / (n + 1).

          Поэтому код функции должен иметь вид

          static double prodDrob (int n) {

          return 1./(n+1)

          }

          Листинг 3.1.13.3. Задача «Произведение дробей»: исправленный вариант

          Мораль проста: даже если задача кажется очевидной, не стоит сразу программировать первый вариант, который приходит в голову. Стоит задуматься над сутью задачи и подумать, нет лучших вариантов. Особенно это касается кода, который часто повторяется — то есть циклов.

        3. оператор варианта

          Часто бывает так, в зависимости от значения определенной переменной надо выполнять различные действия. В принципе, подобная логика может быть реализована на основе условных операций, но это довольно неудобно и приводит к громоздкого кода. Оператор варианта switch (часто называют переключателем) является стандартным решением этой проблемы.

          Рассмотрим небольшой пример.

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

          i — увеличить значение переменной на 1, d — уменьшить значение на 1,

          u — восстановить значения по умолчанию (пусть это значение будет равно 10); q — завершить выполнение программы.

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

          Эту задачу можно решать разными способами; выберем один из самых простых. Реализуем потенциально бесконечный цикл, на каждой итерации которого будем вводить очередную команду. Если длина этой строки равен 1 — берем ее первый символ и реализуем нужные действия в зависимости от значения этого символа.

          Программа может иметь вид: package interactive; import java.util.Scanner;

          public class Interactive {

          public static void main (String [] args) {final int defaultValue = 10;

          final String message = «Please enter your command»; final String errorMessage = «Please enter correct

          data «;

          int currentValue = defaultValue; boolean exit = false;

          Scanner scan = new Scanner (System.in) while (!exit) {

          System.out.println ( «Current value is

          «+ CurrentValue)

          System.out.println (message); String command = scan.nextLine ();

          if (command.length ()!= 1)

          System.out.println (errorMessage)

          else

          {Char cmd = command.charAt (0); switch (cmd) {

          case ‘i’: currentValue ++; break; case ‘d’: currentValue—; break;

          case ‘u’: currentValue = defaultValue;

          break;

          case ‘q’: exit = true; break; default:

          System.out.println (errorMessage) break;

          }

          }

          }

          }

          }

          Листинг 3.1.14.1. Интерактивное управление — иллюстрация оператора варианта

          Здесь будет меняться переменная currentValue. Мы ввели также три константы: defaultValue для значения по умолчанию, а также message и errorMessage для сообщений.

          Потенциально бесконечный цикл организовано достаточно привычным образом. Мы ввели управляющую логическую переменную

          boolean exit = false;

          цикл имеет вид

          while (!exit) {…}

          В соответствии с логикой программы, при определенных условиях в теле цикла переменная exit приобретает значение true, и это означает выход из цикла и завершение программы.

          Первый символ строки получаем с помощью вызова

          char cmd = command.charAt (0);

          Центральное место занимает оператор switch:

          switch (cmd) {

          case ‘i’: currentValue ++; break; case ‘d’: currentValue—; break;

          case ‘u’: currentValue = defaultValue; break; case ‘q’: exit = true; break;

          default: System.out.println (errorMessage) break;

          }

          Мы видим, что в операторе switch содержится несколько разделов case — ветвей, которые соответствуют тому или иному смысле управляющей переменной cmd и задают действия, которые должны быть выполнены в этом случае. Есть также раздел default, который соответствует любому другому значению cmd.

          Обратите внимание на инструкции break в конце каждого раздела. Если их опустить, то произойдет так называемый «провал» — после выполнения раздела выполнения перейдет к следующему.

        4. функции

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

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

          static double kvadr (double x) {return x * x;

          }

          Листинг 3.1.15.1. Функция, которая вычисляет квадрат действительного числа

          Наша функция будет называться kvadr. Она принимает один параметр действительного типа и имеет возвращать истинное значение. В данном случае функция должна быть описана как static, поскольку она вызывается из функции main, которая тоже является статичной.

          Оператор return определяет результат выполнения функции, который будет получен кодом, ее вызывает.

          Вся программа может иметь вид

          package demofunctions;

          import java.util.Scanner; public class DemoFunctions {

          public static void main (String [] args) {Scanner sc = new Scanner (System.in) System.out.println ( «Enter your number») double v = sc.nextDouble ();

          System.out.println ( «The square of» + v + «is» + kvadr (v)

          }

          static double kvadr (double x) {return x * x;

          }

          }

          Листинг 3.1.15.2. Иллюстрация использования функций

        5. строки

    Интуитивно строка — это некоторая последовательность символов. Строка Java — это экземпляр класса

    String.

    Сюрпризом для программистов на C может стать то, что строка Java нельзя рассматривать как массив символов. Например, как вывести на экран первый символ строки? вариант

    String s = «qwerty»; System.out.println (s [0]);

    Листинг 3.1.16.1. Вывод первого символа строки: характерная ошибка является незаконным и приводит к ошибке компиляции.

    Вместо этого следует использовать метод charAt:

    String s = «qwerty»; System.out.println (s.charAt (0));

    Листинг 3.1.16.2. Вывод первого символа строки: исправленный вариант

    Строки Java имеют ряд характерных особенностей. В этом начальном осмотре необходимо обратить внимание на следующие:

      • для конкатенации (сцепления) строк существует метод concat (), но зато можно использовать операцию +;
      • для сравнения строк на равенство вместо операции == следует использовать метод

        equals.

        Приведем небольшой пример. Что выведет программа

        String first = «qwerty»; String s1 = «qwer»; String s2 = «ty»;

        String second = s1 + s2; System.out.println (second) System.out.println (first == second)

        Листинг 3.1.16.3. Сравнение строк с помощью ==

        выводится

        qwerty false

        Несмотря на то, что строки first и second фактически имеют одинаковое содержание (qwerty), выражение first == second ошибочно.

        Ситуация изменится, если заменить == на метод equals.

        String first = «qwerty»; String s1 = «qwer»; String s2 = «ty»;

        String second = s1 + s2; System.out.println (second) System.out.println (first.equals (second))

        Листинг 3.1.16.4. Сравнение строк с помощью метода equals Теперь выводится

        qwerty true

        Класс String содержит значительное количество функций; читателю предлагается ознакомиться с ними самостоятельно.

        Раздел 3.2. Классы и объекты

            1. простейший пример

              Как для любой другой объектно-ориентированного языка, понятие класса и объекта

              {Экземпляру класса) играют для Java ключевую роль. Если процедурные особенности Java мало чем отличаются от аналогичных рис С ++, то объектная модель Java имеет ряд существенных отличий от С ++ — и по синтаксису, и по семантике.

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

              Начнем с небольшого примера создания класса и его использования.

              Иногда можно встретить утверждение о том, что класс — это нечто, что объединяет данные и функции для работы с ними (методы класса). Подобные представления очень базовыми и примитивными, но в этом параграфе мы временно примем эту точку зрения. При этом мы полностью абстрагируемся от всех вопросов, связанных с проектированием и анализом предметной области, инкапсуляцией данных и др. — это просто некоторое синтаксически корректный класс.

              Создадим класс PrimitiveClass:

              package democlass;

              public class PrimitiveClass {int a;

              int b;

              PrimitiveClass (int aa, int bb) {a = aa;

              b = bb;

              }

              void inform () {System.out.println ( «a =» + a + «; b =» + b)

              }

              }

              Листинг 3.2.1.1. простой класс

              В этом классе описаны две переменные (a и b; обе типа int) — это поля класса; данные, с которыми могут работать функции, описанные в классе (в терминологии объектно ориентированного программирования — методы класса). В классе содержится один метод inform, который просто выводит на экран значения полей.

              Кроме того, в классе содержится конструктор, имеет вид:

              PrimitiveClass (int aa, int bb) {a = aa;

              b = bb;

              }

              Конструктор — это специальный метод, имя которого совпадает с именем класса. В конструкторе содержатся действия, которые должны выполняться при создании нового экземпляра класса. В данном случае конструктор — это функция с двумя аргументами, которая устанавливает значения полей.

              Метода main () в этом классе нет.

              Для использования этого класса создадим другой класс DemoClass (по аналогии с процедурным программированием назовем его основным классом, или же основной программой). Этот класс уже быть иметь точку входа в программу — метод main ().

              package democlass; public class DemoClass {

              public static void main (String [] args) {PrimitiveClass ekz = new PrimitiveClass (10,20) ekz.inform ();

              System.out.println (ekz.a + «» + ekz.b)

              }

              }

              Листинг 3.2.1. 2. Демонстрационная программа, которая использует класс Программа выводит:

              a = 10; b = 20 20 октября

              Что происходит в этой программе? С помощью операции new создается экземпляр класса с конкретными значениями полей:

              PrimitiveClass ekz = new PrimitiveClass (10,20) После этого на экземпляре ekz можно вызвать методы: ekz.inform ();

              Собственно говоря, пока мы делаем с классом, созданным нами, точно то же, что раньше делали с библиотечными классами.

              Далее программа еще раз выводит поля экземпляра — но теперь она обращается к ним напрямую:

              System.out.println (ekz.a + «» + ekz.b)

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

            2. Селекторы и модификаторы

        Прежде всего — код, приведенный в предыдущем параграфе, грубо нарушает требования инкапсуляции (отделение интерфейса от реализации). Считается, что поля класса — это в основном детали реализации. Поэтому, если с полями класса действительно нужно работать, нужно делать это через соответствующие методы — и таким образом контролировать доступ к ним.

        Таким образом, поля класса следует объявлять закрытыми (private), а операции с ними осуществлять через методы. Такие методы, скорее всего, будут общедоступными (public). С соблюдением этих требований код класса мог бы выглядеть так:

        package democlass;

        public class AdvancedClass {

        private int a;

        private int b;

        public AdvancedClass (int aa, int bb) {a = aa;

        b = bb;

        }

        public int getA () {return a;

        }

        public void setA (int a) {this.a = a;

        }

        public int getB () {return b;

        }

        public void setB (int b) {this.b = b;

        }

        }

        Листинг 3.2.2.1. Класс по селекторами и модификаторами Рассмотрим одно из полей (пусть это будет поле a): private int a;

        ситуация с другими полями аналогична. Оно имеет модификатор доступа private (закрытое), и поэтому напрямую обращаться к нему из-за пределов класса нельзя (мы это увидим далее).

        Для работы с ним есть два метода с модификатором public, которые могут вызываться из внешнего кода:

        public int getA () {

        return a;

        }

        public void setA (int a) {this.a = a;

        }

        Метод getA () для получения значения поля принято называть селектором, а метод

        setA () для установки нового значения — модификатором.

        Обратите внимание на названия этих методов — они непосредственно связаны с названием поля. Так, для поля a селектор принято называть getA (), а модификатор — setA ().Учитывая это селекторы часто называют Геттера, а модификаторы — сеттер.

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

        public static void main (String [] args) {AdvancedClass ekz = new AdvancedClass (10,20) System.out.println (ekz.a + «» + ekz.b)

        }

        Листинг 3.2.2.2. Поля закрыты — ошибка компиляции

        Поля a и b объявлены как private, и поэтому компилятор выдает сообщение об ошибке.

        Но значения полей можно получить с помощью селекторов:

        public static void main (String [] args) {AdvancedClass ekz = new AdvancedClass (10,20) System.out.println (ekz.getA () + «» + ekz.getB ());

        }

        Листинг 3.2.2.3. Для получения значений полей используем селекторы

        С помощью же модификатора можно установить новое значение поля, например:

        ekz.setA (50);

        Здесь значение поля a будет установлено в 50. 3.2.3. дальнейшее развитие

        Класс AdvancedClass из листинга 3.2.2.1 имеет еще не совсем общепринятый вид.

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

        В-третьих, конструктор с параметрами чаще пишут иным образом. В нашем случае он будет иметь вид

        public AdvancedClass (int a, int b) {this.a = a;

        this.b = b;

        }

        Листинг 3.2.3.1. Типичный вид конструктора с параметрами

        Слово this означает «этот экземпляр», точнее «ссылка на этот экземпляр». Здесь оно необходимо для того, чтобы отличить поля класса от параметров конструктора.

        В-третьих, для того, чтобы предусмотреть возможность для удобного вывода экземпляров класса на экран, обычно используется переопределения метода toString (). Подробнее об этом будет идти дальше.

            1. наследование

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

              В первом приближении, подклассы могут наследовать поля и методы от родительских классов (суперклассов). Рассмотрим небольшой иллюстративный пример.

              Пусть дано класс Base, в котором описан метод metod ():

              class Base {

              public void metod () {System.out.println ( «Ku-ku»)

              }

              }

              Создадим производный класс Sub, который наследуют от Base (синтаксически для отношения наследования используется ключевое слово extends):

              class Sub extends Base {

              }

              Как мы видим, в этом классе нет никаких методов. Но за счет наследования методы, описанные в родительском классе Base, доступны и для этого подкласса. Проверим это.

              Создадим экземпляр класса Sub: Sub sub = new Sub (); Вызовем метод metod (): sub.metod ();

              Этот код будет нормально откомпилированный и выполнен и выведет на экран строку

              Ku-ku.

              Приведем всю программу целиком: package demoinherit; public class Demoinherit {

              public static void main (String [] args) {Sub sub = new Sub ();

              sub.metod ();

              }

              }

              class Base {

              public void metod () {System.out.println ( «Ku-ku»)

              }

              }

              class Sub extends Base {

              }

              Листинг 3.2.4.1. Наследование методов подклассов

              Методы суперкласса могут переопределяться подклассами. Изменим класс Sub следующим образом:

              class Sub extends Base {

              @Override

              public void metod () {

              System.out.println ( «Ku-ku from subclass») ;;

              }

              }

              Листинг 3.2.4.2. Переопределения методов в подклассе

              Запись @Override — это так называемая инструкция, которая сигнализирует компилятору о том, что данный метод — это переопределен метод сверхкласса.

              Теперь метод metod () переопределен в подклассе, и код

              Sub sub = new Sub ();

              sub.metod ();

              приводит к выводу Ku-ku from subclass.

              Конечно, наследование — это очень большая и серьезная тема, и в этом кратком обзоре дано лишь очень первоначальное и интуитивное представление о нем.

            2. Пример: наследование метода main

              Мы уже видели, что если класс рассматривается как приложение, выполняется под управлением операционной системы, в нем обязательно мажь быть метод main ().Но этот метод может быть определен в сверхкласса …

              Попробуем.

              Создадим класс Base с методом main ():

              public class Base {

              public static void main (String [] args) {

              System.out.println ( «Main is not necessary for subclasses»)

              }

              }

              Листинг 3.2.5.1. Суперкласс с методом main Создадим пустой подкласс Sub:

              public class Sub extends Base {

              }

              Листинг 3.2.5.2. Класс, который наследуют от суперкласса с методом main () Откомпилируем оба класса обычным образом и запустим Sub на выполнение: java Sub

              Программа будет выполнена и выведет

              Main is not necessary for subclasses

              В самом классе Sub нет методов main (), но вызывается метод main ()

              суперкласса.

              Заметим этот пример является чисто иллюстративный, и мы не рекомендуем так делать. Кроме того, метод main () является статическим, и его наследования имеет особенности, которые заслуживают отдельного рассмотрения.

            3. класс Object

              Класс Object (точнее, java.lang.Object, класс Object из пакета java.lang) имеет для языка Java особое значение. Этот класс находится на вершине иерархии классов, и все другие классы являются производными от него — прямо или косвенно.

              В классе Object определен ряд методов; многие из них переопределяются в подклассах для предоставления им необходимой функциональности. Рассмотрим несколько примеров.

              Метод equals ().Предназначен для проверки равенства объектов. В самом классе Object этот метод определен так, что он сравнивает только ссылки на объекты. Для того, чтобы сравнивать содержание самих объектов, этот метод должен быть переопределен в соответствующем классе. Пример использования этого метода приведен в п. 3.2.7.

              Метод toString (). Возвращает строковое представление объекта; широко применяется при выводе объектов. Подробнее об этом методе будет идти в п.3.2.8.

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

            4. Элементарный демонстрационный класс

              Проиллюстрируем методы equals () и toString () на очень простом примере. С этой целью создадим очень простой класс с одним полем value, необходимыми конструкторами, а также «стандартными» селекторами и модификаторами:

              package elementarydemo;

              public class ElementaryClass {private int value;

              public ElementaryClass () {

              }

              public ElementaryClass (int value) {this.value = value;

              }

              public int getValue () {return value;

              }

              public void setValue (int value) {this.value = value;

              }

              }

              Листинг 3.2.7.1. Элементарный класс для демонстрации методов equals () и toString ()

              Содержательно такой класс может существовать для хранения информации о определенную величину и манипулирования ею. Тогда значение этой величины является основной характеристикой конкретного экземпляра класса и фактически определяет содержание этого экземпляра.

            5. Метод equals ()

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

              Для этого создадим два экземпляра класса ElementaryClass с одинаковыми значениями поля value. Мы хотели бы считать их равными между собой. Будем проверять их на равенство; сначала воспользуемся оператором ==.

              package elementarydemo; public class DemoEquals {

              public static void main (String [] args) {ElementaryClass o1 = new ElementaryClass (10); ElementaryClass o2 = new ElementaryClass (10); System.out.println (o1 == o2)

              }

              }

              Листинг 3.2.8. 1. Проверка равенства объектов: использование оператора ==

              Программа выводит на экран false. Действительно, оператор == не анализирует значения полей объектов; она проверяет только идентичность самих объектов. Этот вопрос требует более основательного анализа (см. П.), Но сейчас можно сказать следующее. Экземпляры o1 и o2 — это разные объекты, и поэтому выражение o1 == o2 равен false.

              Теперь проверим равенство объектов с помощью метода equals ():

              package elementarydemo; public class DemoEquals {

              public static void main (String [] args) {ElementaryClass o1 = new ElementaryClass (10); ElementaryClass o2 = new ElementaryClass (10); System.out.println (o1.equals (o2))

              }

              }

              Листинг 3.2.8.2. Проверка равенства объектов: использование equals Программа снова выводит false. Попробуем разобраться, почему.

              Метод equals () — это метод класса Object для проверки объектов на равенство. но

              каким должен быть этот метод? Класс Object ничего не знает о возможных подклассы и о полях этих подклассов. Поэтому для метода equals () класса Object не остается ничего лучшего, чем делать то же, что делает операция сравнения ==, то есть проверять идентичность объектов.

              Для того, чтобы экземпляры класса можно было сравнивать по содержанию, необходимо задать критерий такого сравнения. Для этого в классе должно быть переопределен метод equals ().

              попробуем:

              package elementarydemo;

              public class ElementaryClass {private int value;

              public ElementaryClass () {

              }

              public ElementaryClass (int value) {this.value = value;

              }

              public int getValue () {return value;

              }

              public void setValue (int value) {this.value = value;

              }

              @Override

              public boolean equals (Object obj) {ElementaryClass other = (ElementaryClass) obj; return this.getValue () == other.getValue ();

              }

              }

              Листинг 3.2.8.4. Переопределение метода equals

              Такое переопределение метода equals () не является полностью общепринятым, но для наших целей этого пока будет достаточно.

              Теперь в результате выполнения кода

              ElementaryClass o1 = new ElementaryClass (10); ElementaryClass o2 = new ElementaryClass (10); System.out.println (o1.equals (o2))

              на экран выводится true.

              Рассмотрим переопределен метод equals () более подробно:

              @Override

              public boolean equals (Object obj) {ElementaryClass other = (ElementaryClass) obj; return this.getValue () == other.getValue ();

              }

              Метод возвращает результат вычисления выражения this.getValue () == other.getValue (), что вполне логично. Но параметр этого метода имеет тип Object, и поэтому для обращения к полям подкласса мы должны были осуществить приведение типов:

              Обратим еще раз внимание на операцию

              return this.getValue () == other.getValue ();

              Здесь метод класса для доступа к полям использует селекторы. Любой метод имеет полный доступ ко всем полям этого класса, в том числе тем, которые имеют модификатор private. Поэтому можно было бы написать и так:

              return this.value == other.value;

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

            6. Метод toString ()

              Вернемся к классу ElementaryClass из листинга 3.2.7.1; для того, чтобы этот код был «под рукой», повторим его еще раз.

              package elementarydemo;

              public class ElementaryClass {private int value;

              public ElementaryClass () {

              }

              public ElementaryClass (int value) {this.value = value;

              }

              public int getValue () {return value;

              }

              public void setValue (int value) {this.value = value;

              }

              }

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

              package elementarydemo; public class DemoToString {

              public static void main (String [] args) {ElementaryClass ekz = new ElementaryClass (10); System.out.println (ekz)

              }

              }

              Листинг 3.2.9.1. Попытка вывести на экран экземпляр класса

              Но вывод будет достаточно неловко. Оно может быть, например, таким:

              elementarydemo.ElementaryClass @ 1db9742 Что происходит?При вызове System.out.println (ekz)

              на объекте ekz неявно вызывается метод toString ().Поэтому для того, чтобы сделать вывод более осмысленным, в классе ElementaryClass нужно задать, каким образом надо выводить его экземпляры, а для этого указанный метод.Класс по переопределен методом toString () может выглядеть, например, следующим образом:

              package elementarydemo;

              public class ElementaryClass {

              private int value;

              public ElementaryClass () {

              }

              public ElementaryClass (int value) {this.value = value;

              }

              public int getValue () {return value;

              }

              public void setValue (int value) {this.value = value;

              }

              @Override

              public String toString () {return «value =» + value;

              }

              }

              Листинг 3.2.9.2. Переопределения метода toString ()

              Метод toString () формирует строчное представление объекта, и мы можем сделать сформировать это представление так, как считает нужным.

              Теперь в результате выполнения кода

              ElementaryClass ekz = new ElementaryClass (10); System.out.println (ekz)

              будет выведено

              value = 10

            7. «Канонический» состав класса

              Как мы видели, даже самый класс должен быть написан грамотно. В соответствии с общепринятыми требованиями, типичный класс должен содержать:

      • поля, которые в соответствии с требованиями инкапсуляции нужно описывать как private (кроме отдельных случаев)
      • конструкторы: один конструктор без параметров и конструкторы, которые позволяют задать значение своих параметров;
      • селекторы и модификаторы по мере необходимости; можно представить себе ситуации, когда селекторы и / или модификаторы не стоит делать публичными, или же они вообще не нужны (например, если значение поля должно исчисляться автоматически и в дальнейшем не меняться).

        Типичным является определение в классе методов toString () и equals (). Если определяется equals () должен определяться и метод hashCode (). Эта функция возвращает целое число, которое можно

        использовать для эффективного поиска объектов в памяти. Последнее правило связано с тем, что для равных объектов хеш-коды должны также совпадать.

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

            1. Использование IDE для автоматического конструирования классов

              Мы видим, что программирование классов требует рутинных операций, связанных с написанием большого количества методов — конструкторов, селекторов / модификаторов и др. К счастью, значительную часть этого процесса можно автоматизировать при использовании таких IDE, как Eclipse или NetBeans. Покажем, как можно ускорить создание класса в среде Eclipse; возможности NetBeans аналогичны.

              Создадим обычным образом проект в Eclipse. В этом проекте создадим проект, а в нем класс с двумя целочисленными полями first и second:

              package autogenerate;

              public class AutoGenerated {

              private int first; private int second;

              }

              Листинг 3.2.11.1. Автоматическая генерация элементов класса: первый шаг

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

              image

              Меню Source содержит ряд функций, позволяющих создавать необходимые компоненты кода. Например, создадим конструктор без параметров. Выбираем функцию Generate Constructor Using Fields … Мы должны увидеть окошко, подобно следующему:

              image

              Не будем отмечать никаких полей; установим опцию Omit call to default constructor super () (опустить вызов конструктора суперкласса по умолчанию).

              Будет сгенерирован код

              public AutoGenerated () {

              }

              Аналогично, если отметить оба поля:

              будет создан «стандартный» конструктор, устанавливающий значение отмеченных полей:

              public AutoGenerated (int first, int second) {this.first = first;

              this.second = second;

              }

              Для создания селекторов и модификаторов выбираем пункт Generate Getters and Setters … В окне

              image

              отмечаем, какие именно селекторы и модификаторов нужно сгенерировать.

              Здесь мы выбрали все селекторы и модификаторы. Не забываем о том, что модификатор доступа к этим методам должно быть public.

              Подобным образом создаем методы toString (), equals (), hashCode ().

              Обратите внимание на код сгенерированного метода equals ():

              @Override

              public boolean equals (Object obj) {if (this == obj)

              return true; if (obj == null)

              return false;

              if (getClass ()!= Obj.getClass ()) return false;

              AutoGenerated other = (AutoGenerated) obj; if (first!= Other.first)

              return false;

              if (second!= Other.second) return false;

              return true;

              }

              Листинг 3.2.11.2. Сгенерированный код метода equals ()

              Здесь, кроме значений полей, проверяется, есть объекты, которые сравниваются, экзепляры одного и того же класса. Для получения информации о классе объекта используется функция getClass ().

              Приведем окончательный код класса:

              package autogenerate;

              public class AutoGenerated {private int first;

              private int second;

              public AutoGenerated () {

              }

              public AutoGenerated (int first, int second) {this.first = first;

              this.second = second;

              }

              public int getFirst () {return first;

              }

              public void setFirst (int first) {this.first = first;

              }

              public int getSecond () {return second;

              }

              public void setSecond (int second) {this.second = second;

              }

              @Override

              public String toString () {

              return «AutoGenerated [first =» + first + «, second =» + second + «]»;

              }

              @Override

              public int hashCode () {final int prime = 31; int result = 1;

              result = prime * result + first; result = prime * result + second; return result;

              }

              @Override

              public boolean equals (Object obj) {if (this == obj)

              return true; if (obj == null)

              return false;

              if (getClass ()!= Obj.getClass ()) return false;

              AutoGenerated other = (AutoGenerated) obj; if (first!= Other.first)

              return false;

              if (second!= Other.second) return false;

              return true;

              }

              }

              Листинг 3.2.11.3. Окончательный код класса с автоматически сгенерированными основными методами

            2. интерфейсные типы

        В Java на уровне языка поддерживается понятие интерфейса. Интерфейсы делают программу более гибкой и широко используются в практическом программировании.

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

        Рассмотрим небольшой пример. Создадим интерфейс Interf, в котором описан метод

        foo ():

        package demointerfaces; public interface Interf {

        public void foo ();

        }

        Листинг 3.2.12.1. Интерфейс с одним методом Обратите внимание: метод foo () не имеет тела.

        В классе MyClass, который реализует этот интерфейс, должна содержаться конкретная

        реализация метода inform (), описанного в интерфейсе, поэтому следующий код не будет откомпилированный:

        package demointerfaces;

        public class MyClass implements Interf {

        }

        Листинг 3.2.12.2. Неправильная имплементация интерфейса: необходимый метод не реализован

        Правильная реализация может быть, например, такой:

        package demointerfaces;

        public class MyClass implements Interf {

        @Override

        public void foo () {System.out.println ( «Ku-ku!»)

        }

        }

        Листинг 3.2.12.3. Правильная имплементация интерфейса 3.2.13.Перечисление.

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

        приобретать лишь значения из некоторого перечня. Например, цвет может быть зеленым, красным, голубым и тому подобное. Аналогично, можно перечислить возможные марки автомобилей (Mercedes, Honda, Ferrari, Slavuta и т.п.), дни недели, названия месяцев и тому подобное.

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

        В Java начиная с версии 1.5 существует прямая поддержка перечисляемых типов, которые позволяют непосредственно оперировать с константами этих типов. Для описания перечисленных используется ключевое слово enum.

        Рассмотрим пример. Введем перечисляемый тип Car, который описывает марки автомобилей:

        enum Car {

        Slavuta, Ferrari, Mersedes, Honda

        }

        Листинг 3.2.13.1. Пример перечислений типа

        Проиллюстрируем использование этого типа. Сначала с помощью вызова

        Car [] cr = Car.values ();

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

        for (Car c: cr) {switch (c) {

        case Slavuta: claim = «Крутая тачка»; break;

        case Ferrari: claim = «Тоже ничего»; break;

        case Mersedes: claim = «Зачем Вам и колымага?»; Break;

        case Honda: claim = «А в Китае тоже делают машины»;

        }

        System.out.println (c.ordinal () + «.» + C + «-» + claim)

        }

        Вся программа имеет вид:

        package demoenum; enum Car {

        Slavuta, Ferrari, Mersedes, Honda

        }

        public class Demoenum {

        public static void main (String [] args) {Car [] cr = Car.values ​​();

        String claim = «»; for (Car c: cr) {switch (c) {

        case Slavuta: claim = «Крутая тачка»; break; case Ferrari: claim = «Тоже ничего»; break;

        case Mersedes: claim = «Зачем Вам и колымага?»; Break; case Honda: claim =» А в Китае тоже делают машины «;

        }

        System.out.println (c.ordinal () + «.» + C + «-» + claim)

        }

        }

        }

        Листинг 3.2.13.2. Создание и использование перечисляемых типов В результате получаем такой вывод:

        1. Slavuta — Крутая тачка 1.Ferrari — тоже ничего

          2.Mersedes — Зачем Вам и колымага? 3.Honda — А в Китае тоже делают машины

          3.2.14. исключение

          Исключение — это нестандартные, часто ошибочные ситуации, которые могут возникать при выполнении программ. Это может быть, например, деление на ноль, ошибка ввода-вывода и т. Исключение рассматривается как ошибка времени выполнения; сбой, который в случае, если его не обработать, приведет к аварийной остановке программы (или отдельного потока).

          Каждое исключение является экземпляром некоторого класса; на вершине иерархии классов исключений находится класс Throwable. Чаще всего (хотя и не обязательно) виключення- это экземпляр класса Exceptionили одного из его подклассов.

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

          Если в ходе выполнения метода может возникнуть контролируемое исключения, следует действовать одним из двух способов: либо предусмотреть его обработку в самом методе с помощью конструкции try-catch-finally, или сообщить компилятор о том, что метод будет выбрасывать исключения наружу с помощью ключевого слова throws .

          Вернемся к известному нам примеру с введением строк. код

          import java.io.BufferedReader; import java.io.InputStreamReader;

          public class DemoExceptions {

          public static void main (String [] args) {

          BufferedReader br = new BufferedReader (new InputStreamReader (System.in))

          System.out.println ( «Please enter the string») String st = br.readLine (); System.out.println ( «You entered:» + st)

          }

          }

          Листинг 3.2.14.1. Ошибка компиляции: отсутствие обработки контролируемых исключений

          НЕ компилируется через вышеуказанную причину. Считается, что во время ввода-вывода могут возникать ошибки, и поэтому метод readLine () класса BufferedReader может генерировать контролируемое исключение IOException.

          Что можно сделать?

          Способ 1. Мы можем решить, что нас удовлетворяет стандартная реакция системы (программа останавливается и выводится стандартное сообщение интерпретатора). Тогда можно просто изменить название метода main с использованием ключевого слова throws:

          package demoexceptions;

          import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;

          public class DemoExceptions {

          public static void main (String [] args) throws IOException {BufferedReader br = new BufferedReader (new

          InputStreamReader (System.in))

          System.out.println ( «Please enter the string») String st = br.readLine (); System.out.println ( «You entered:» + st)

          }

          }

          Листинг 3.2.14.2. Использование ключевого слова throws

          Способ 2. Мы можем предсказать обработку исключительной ситуации в самой программе. Включим код, который может генерировать исключения в блок try и перехватим исключения с помощью блока catch:

          package demoexceptions;

          import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;

          public class DemoExceptions {

          public static void main (String [] args) {

          BufferedReader br = new BufferedReader (new InputStreamReader (System.in))

          System.out.println ( «Please enter the string»)

          try {

          String st = br.readLine (); System.out.println ( «You entered:» + st)

          }

          catch (IOException e) {System.out.println ( «Error in reading»)

          }

          }

          }

          Листинг 3.2.14.3. Обработка исключений в блоке try-catch

          Более общо, перехватывать и обрабатывать можно несколько типов исключений, тогда каждому типу исключительных ситуаций будет отвечать отдельный блок catch.

          В схеме обработки исключений важную роль играет еще один элемент: блок finally. Общую схему обработки исключений на основе конструкции try-catch-finally можно охарактеризовать следующим образом:

          try {…}

          catch (исключение e) {…}

          finally {…}

          Блок finally выполняется всегда (кроме случаев, когда останавливается вся программа или данный поток), независимо от того, случилось исключения или нет, и что именно происходило в блоках try или catch.Поэтому в блок finally рекомендуется включать действия, которые должны быть выполнены в любом случае.Что касается операций ввода-вывода, к таким действиям относится закрытия потока. Потоки и другие ресурсы, которые использовались блоком try, должны закрываться и освобождаться в блоке finally.

          Посмотрим на более развернутый пример. С клавиатуры вводится действительное число и исчисляется экспонента от него. Здесь могут возникать различные типы ошибок: ошибки чтения, ошибки закрытия потока, ошибки преобразования в случае, если не удается превратить введенную строку на действительное число, арифметические ошибки. Поэтому одному блоку try соответствует несколько блоков catch. Обратите внимание также на блок try, включен в блок finally.

          import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader;

          public class Readwrite {

          public static void main (String [] args) {System.out.println ( «Enter the real value»)

          BufferedReader br = new BufferedReader (new InputStreamReader (System.in))

          try {

          String s = br.readLine ();

          double d = Double.parseDouble (s) double dexp = Math.exp (d) System.out.println ( «Result is» + dexp)

          }

          catch (IOException ioe) {System.out.println ( «Input error»);} catch (NumberFormatException ioe) {

          System.out.println ( «Please enter the proper value»)

          }

          catch (ArithmeticException are) {System.out.println ( «Error in calculations»)

          }

          catch (Exception e) {

          System.out.println ( «Something strange and unbelievable»)

          }

          finally {try {if (br!= Null) br.close ();

          else System.out.println ( «Inproper closing of the stream»);} catch (Exception e)

          {System.out.println ( «The God is against you»);}

          }

          }

          }

          Листинг 3.2.14.4. Введение числа: много возможных исключений

          Раздел 3.3. Примитивные и объектные типы

              1. Разница между примитивными и объектными типами

                Все типы в Java делятся на две категории: примитивные (простые) и объектные (указателей).

                В Java существует 8 примитивных типов:

      • целочисленные типы (byte, short, int, long)
      • действительны типа (float, double)
      • символьный тип (char)
      • логический, или буле вой, тип (boolean).

        Переменные примитивных типов ведут себя подобно их аналогов в С ++ или в Паскале.

        операция

        int a;

        приводит к тому, что на стеке выполнения программы под переменную выделяется память нужного размера. В таком варианте переменная a остается неинициализированной (если это локальная переменная, а не поле класса). Но если написать

        int a = 10;

        к соответствующему участку памяти запишется число 10.

        В результате операции

        int b = a;

        на стеке будет создана еще одна переменная b, к которой будет скопировано значение переменной

        a.

        К объектных типов принадлежат классы (в том числе строки), интерфейсы, а также

        массивы.

        Во-первых, переменные объектных типов создаются не на стеке, а в динамическом купе. Во-вторых, все операции с объектными переменными — это операции по указателей.

            1. Есть ли в Java указателей

        Официально указателей в Java нет. Действительно, в Java отсутствуют разыменования, нет арифметики указателей. Но переменные объектных типов ведут себя именно как указателей.

        Создадим класс MyClass с целочисленным полем a и полным джентльменским набором: конструкторы, сеттеры и геттеры и метод toString ():

        package javapointer; public class MyClass {

        public MyClass () {

        }

        public MyClass (int a) {this.a = a;

        }

        public int getA () {return a;

        }

        public void setA (int a) {this.a = a;

        }

        @Override

        public String toString () {return » + a;

        }

        private int a;

        }

        Что можно сказать о следующей операции:

        MyClass ekz;

        Это не создание экземпляра класса — создается только указателей соответствующего типа.

        Новый экземпляр можно создать с использованием операции new:

        MyClass ekz = new MyClass (10);

        Теперь будет создан экземпляр класса MyClass, и на него будет установлен указателей ekz. Вообще, доступ к экземплярам классов осуществляется только через указателей на них.

        Если проводить аналогии с C ++, то код Java

        MyClass ekz = new MyClass ();

        является по сути эквивалентным следующем кода в С ++:

        MyClass * ekz = new MyClass ();

        К чему приведет операция:

        MyClass other = ekz;

        Новый экземпляр не создается, никакого копирования полей не происходит. Просто будет создан еще один указателей other, который указывает на тот же объект (то есть осуществляется копирования указателей; указателей other придается значение указателей ekz).

        В соответствии с этим, операции с использованием одного указателей равнозначны

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

        MyClass ekz = new MyClass (10); MyClass other = ekz; other.setA (20) System.out.println (ekz)

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

        Для иллюстрации выполним еще одно упражнение — ручная реализация списка. напишем

        приложение, которое создает связанный список целых чисел от 1 до 10, а затем просматривает этот список. При этом коллекции не используем; нужно ограничиться только низкоуровневыми операциями с указателей.

        Если писать подобную программу на C / C ++, основная идея заключается в следующем. Нужно создать структуру или класс с двумя полями: именно число и указателей на следующую компонента. Сама программа может иметь вид (мы специально выбрали стиль, максимально приближенный к стилю Java):

        #include <iostream>

        #include <conio.h> using namespace std;

        struct Node {

        };

        void main ()

        {

        int value; Node * pointer;

        Node * beg = NULL; Node * p = NULL;

        for (int i = 1; i <= 10; i ++) {p = new Node (); (* P) ​​.value = i;

        (* P) ​​.pointer = beg; beg = p;

        }

        p = beg;

        for (int i = 1; i <= 10; i ++) {cout << (* p) .value << «»; p = (p *). pointer;

        }

        _getch ();

        }

        Листинг 3.3.3.1. Низкоуровневая реализация списка на С ++ с использованием указателей Программа выведет:

        10 9 8 7 6 5 4 3 2 1

        (Числа идут в обратном порядке в соответствии с стекового принципу).

        Если написать подобную программу на Java, то основная ее часть по сути будет отличаться лишь отсутствием розименувань (для простоты мы не придерживаемся требований инкапсуляции):

        package javapointer; public class Main {

        public static void main (String [] args) {Node beg = null;

        Node p = null;

        for (int i = 1; i <= 10; i ++) {p = new Node (); p.value = i; p.pointer = beg;

        beg = p;

        }

        p = beg;

        for (int i = 1; i <= 10; i ++) {System.out.print (p.value + «»); p = p.pointer;

        }

        }

        }

        class Node {int value; Node pointer;

        }

        Листинг 3.3.3.2. Низкоуровневая реализация списка на Java 3.3.4. Когда x instanceof Object равен false

        Как мы уже отмечали, на вершине иерархии типов находится класс Object.

        В любом случае результатом операции x instanceof Object является false?

        Часто говорят: в случае, если x является переменной примитивного типа. На самом деле это не так: в этом случае это выражение вызывает ошибку компиляции.

        Поскольку любой класс является потомком Object, для указателей x, что указывает на экземпляр любого класса, значением выражения x instanceof Object является true. И единственная возможность, при которой это выражение равно false — это когда x равен null.

        Раздел 3.4. Наборы данных: массивы и коллекции

            1. Понятие о наборах данных

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

            2. Основные черты массивов

              Мы уже встречались с массивами, в частности при рассмотрении циклов.

              Для создания массива нужно указать тип элементов и задать размер, например:

              int [] mas = new int [10];

              Здесь создается массив целых чисел из 10 элементов.

              Основная операция для работы с массивами — это индексация, то есть доступ к элементу по его номеру. При этом нумерация элементов массива начинается с 0. Например:

              System.out.println (mas [0]); // Вывод на экран первого элемента массива

              mas [1] = 10; // Значение второго элемента устанавливается в 10.

              Для получения длины этого массива следует использовать выражение mas.length

              (Обратите внимание на синтаксис!).

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

              например:

              int [] mas = {10,20,40,50,30};

              Приведем программу, которая иллюстрирует все перечисленные черты. Она создает массив и выводит на экран его длину, а также все элементы массива:

              public class DemoArrays {

              public static void main (String [] args) {

              int [] mas = {10,50,40,20,30};

              System.out.println ( «The length of array is» + mas.length)

              for (int m: mas) {System.out.print (m + «»);

              }

              }

              }

              Листинг 3.4.2.1. Литерально задания и перебор массива Программа выведет

              The length of array is 5 10 50 40 20 30

            3. Знакомство с классом Arrays: сортировка массива

              Много удобных средств для работы с массивами сосредоточено в классе java.util.Arrays. Например, попробуем отсортировать массив из предыдущего листинга:

              package demoarrasys; import java.util.Arrays;

              public class DemoArrays {

              public static void main (String [] args) {int [] mas = {10,50,40,20,30};

              Arrays.sort (mas)

              System.out.println ( «The length of array is» + mas.length)

              for (int m: mas) {System.out.print (m + «»);

              }

              }

              }

              Листинг 3.4.3. 1. Пример сортировки массива Теперь программа выводит отсортированный массив:

              The length of array is 5 10 20 30 40 50

              На самом деле средств сортировки достаточно много. В частности, можно задать критерий сортировки; начиная с Java 8 для этого можно использовать ламбда-выражения. Например, отсортируем массив строк по возрастанию их длин:

              package demoarrasys; import java.util.Arrays;

              public class DemoArrays {

              public static void main (String [] args) {

              String [] mas = { «zzz», «a», «long», «this string is almost a blob», «very long»};

              Arrays.sort (mas, (a, b) -> (a.length () — b.length ()))

              for (String s: mas) {System.out.println (s);

              }

              }

              }

              Листинг 3.4.3.2. Сортировка массива строк с использованием ламбда-выражений Программа выводит

              a zzz long

              very long

              this string is almost a blob

              Здесь критерий сортировки — это выражение (a, b) -> (a.length () — b.length ()).Это и есть ламбда-выражение; он описывает функцию, которая принимает две строки и возвращает разницу их длин. Отложим дальнейший анализ этого кода и пока будем рассматривать его как некую демонстрацию отдельных возможностей.

            4. Динамическое заполнение массива

              Конечно, массивы можно заполнять и динамично, по ходу выполнения программы. Попробуем заполнить массив случайными целыми числами в диапазоне от 0 до 9. Для получения случайных значений используем класс Random из пакета java.util.

              Привычная техника использования класса Random заключается в следующем. Сначала создается собственно генератор случайных чисел как экземпляр этого класса:

              Random rand = new Random ();

              Далее можно вызвать методы для получения очередного случайного числа.

              Например, вызов

              int v = rand.nextInt (10);

              приведет к тому, что переменная v получит случайное целое значение в диапазоне от 0 до 9 включительно.

              Обращаем внимание на то, что память под массив выделяется уже во время выполнения программы. Это означает, что для задания размера массива можно использовать переменные. Воспользуемся этим фактом для того, чтобы и сам массив имел случайный размер, но не превышал 20 (для простоты мы игнорируем тот факт, что полученная длина массива может равняться 0):

              package demoarrasys; import java.util.Random;

              public class DemoArrays {

              public static void main (String [] args) {Random rand = new Random ();

              int len ​​= rand.nextInt (21) // Длина массива System.out.println ( «Length of array is» + len) int [] mas = new int [len]; // Создание массива for (int i = 0; i <len; i ++) {

              mas [i] = rand.nextInt (10); // Очередной элемент

              }

              // Вывод массива for (int m: mas) {

              System.out.print (m + «»);

              }

              }

              }

              Листинг 3.4.4.1. Массив формируется случайным образом

              Конечно, результаты выполнения программы могут меняться от одного запуска к другому. Вывод может быть, например, таким:

              Length of array is 11 6 4 3 7 4 1 3 6 5 0 5

            5. Массив из экземпляров класса

              Экземпляры некоторого класса тоже могут быть элементами массива. Рассмотрим простой пример.

              Создадим простой класс ElemClass с одним полем целого типа, необходимыми конструкторами, селекторами, модификаторами и методом toString ():

              package demoarrasys; public class ElemClass {

              private int n;

              public ElemClass () {

              }

              public ElemClass (int n) {this.n = n;

              }

              public int getN () {return n;

              }

              public void setN (int n) {this.n = n;

              }

              @Override

              public String toString () {return » + n;

              }

              }

              Листинг 3.4.5.1. Экземпляры этого класса будут элементами массива

              Массив из 5 элементов этого класса можно создать следующим образом (при этом для задания длины введем константу LEN):

              final int LEN = 10; // Длина массива

              ElemClass [] mas = new ElemClass [LEN]; // Создание массива

              Заполняем его:

              for (int i = 0; i <LEN; i ++) {

              mas [i] = new ElemClass (i)

              }

              Просматривать массив будем обычным образом. Вся программа имеет вид:

              package demoarrasys;

              public class DemoArrays {

              public static void main (String [] args) {final int LEN = 10; // Длина массива

              ElemClass [] mas = new ElemClass [LEN]; // Создание массива

              for (int i = 0; i <LEN; i ++) {

              mas [i] = new ElemClass (i)

              }

              // Вывод массива

              for (ElemClass m: mas) {System.out.print (m + «»);

              }

              }

              }

              Листинг 3.4.5.2. Программа, которая работает с массивом экземпляров Она выводит:

              0 1 2 3 4 5 6 7 8 9

            6. Проблемы с размером массива

              Массивы как средство хранения последовательностей — это структура достаточно эффективна, но недостаточно гибкая. Хотя и разрешается создавать массивы во время выполнения программы, но при создании массива мы все-таки должны объявить его размер. После этого нам уже не удастся увеличить размер этого массива и добавить к нему еще один элемент.

              Можно реализовать один из двух путей решения этой проблемы:

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

    Рассмотрим следующую задачу: нужно заполнить массив экземплярами класса ElemClass из листинга 3.5.4.1. Поля этого класса должны быть целыми положительными числами. Эти числа вводятся с клавиатуры, но их количество заранее неизвестно. Признаком конца должно быть введение 0.

    Реализуем второй подход: заранее зарезервируем значительный размер массива; обозначим его константой MAXLEN. Для хранения текущего размера введем переменную count; при введении нового элемента эта переменная будет увеличиваться на 1.

    Первый вариант программы, которая заполняет массив, а затем выводит его содержимое на экран, может выглядеть следующим образом (для простоты мы считаем введение гарантированно корректным и игнорируем могут быть исключения)

    package demoarrasys; import java.util.Scanner;

    public class DemoArrays {

    public static void main (String [] args) {final int MAXLEN = 10; // Длина массива

    ElemClass [] mas = new ElemClass [MAXLEN]; //Создание

    массива

    int count = 0; // Текущая длина Scanner sc = new Scanner (System.in) while (true) {

    System.out.println ( «Введите следующее число»);

    int elem = sc.nextInt (); if (elem == 0) break;

    mas [count ++] = new ElemClass (elem)

    }

    // Вывод массива

    for (ElemClass m: mas) {System.out.print (m + «»);

    }

    }

    }

    Листинг 3.4.6.1. Заполнение и вывод массива: неправильное использование модифицированного for

    Для ввода данных реализовали потенциально бесконечный цикл; если очередное введено число равно 0, мы исходим из этого цикла с помощью инструкции break.

    Но вывод программы является не совсем правильным. Так, если мы введем число 15,

    35, 50 и 0 (сигнал конца введения), программа выведет

    15 35 50 null null null null null null null

    то есть выводятся лишние значение null. Это и неудивительно — ведь мы выводим весь зарезервирован массив, а не ту его часть, которая фактически заполнена.

    Возможно, поможет использование для вывода обычного цикла? попробуем:

    package demoarrasys; import java.util.Scanner;

    public class DemoArrays {

    public static void main (String [] args) {final int MAXLEN = 10; // Длина массива

    ElemClass [] mas = new ElemClass [MAXLEN]; //Создание

    массива

    int count = 0; // Текущая длина Scanner sc = new Scanner (System.in)

    while (true) {

    System.out.println ( «Введите следующее число»); int elem = sc.nextInt ();

    if (elem == 0) break;

    mas [count ++] = new ElemClass (elem)

    }

    // Вывод массива

    for (int i = 0; i <mas.length; i ++) {

    System.out.print (mas [i] + «»);

    }

    }

    }

    Листинг 3.4.6.2. Заполнение и вывод массива: неправильный размер массива

    Результат тот же. Действительно, цикл

    for (int i = 0; i <mas.length; i ++) {

    System.out.print (mas [i] + «»);

    }

    является неверным; выражение mas.length возвращает объявлен размер массива, а не фактическое количество введенных элементов.

    Фактический размер заполненной части — это переменная count. Поэтому программу следует переписать следующим образом:

    package demoarrasys; import java.util.Scanner;

    public class DemoArrays {

    public static void main (String [] args) {final int MAXLEN = 10; // Длина массива

    ElemClass [] mas = new ElemClass [MAXLEN]; //Создание

    массива

    int count = 0; // Текущая длина Scanner sc = new Scanner (System.in)

    while (true) {

    System.out.println ( «Введите следующее число»); int elem = sc.nextInt ();

    if (elem == 0) break;

    mas [count ++] = new ElemClass (elem)

    }

    // Вывод массива

    for (int i = 0; i <count; i ++) {System.out.print (mas [i] + «»);

    }

    }

    }

    Листинг 3.4.6.3. Заполнение и вывод массива: исправленный вариант

        1. Переход к коллекциям

          Рассмотрим другой и более рекомендован вариант решения нашей задачи (заполнение массива, размер которого заранее неизвестен) — с использованием коллекций.

          Существует несколько типов коллекций. Для наших целей подойдут последовательности, наиболее употребительным из которых ArrayList из пакета java.util.

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

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

          Объявления коллекции может иметь вид:

          ArrayList <ElemClass> dataSet = new ArrayList <ElemClass> ();

          Здесь тип самой коллекции — ArrayLis t; тип элементов (ElemClass) мы указали в угловых скобках. Размер коллекции указывать не обязательно. Интуитивно ArrayList — это динамический массив, к которому можно добавить столько элементов, сколько нужно.

          Начиная с Java 7, можно использовать упрощенный синтаксис описания коллекций:

          ArrayList <ElemClass> dataSet = new ArrayList <> ();

          Новые элементы в коллекцию можно добавлять с помощью метода add. Перепишем нашу программу с использованием коллекций:

          package demoarrasys;

          import java.util.ArrayList; import java.util.Scanner;

          public class DemoArrays {

          public static void main (String [] args) {

          ArrayList <ElemClass> dataSet = new ArrayList <> ();

          Scanner sc = new Scanner (System.in)

          // Заполнение коллекции while (true) {

          System.out.println ( «Введите следующее число»); int elem = sc.nextInt ();

          if (elem == 0) break;

          dataSet.add (new ElemClass (elem))

          }

          // Вывод коллекции

          for (ElemClass el: dataSet) {System.out.print (el)

          }

          }

          }

          Листинг 3.4.7.1. Заполнение и вывод коллекции

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

        2. Знакомство с многомерными массивами

    В отличие от многих других языков программирования, в Java нет настоящих многомерных массивов. На самом деле многомерный массив Java — это «массив массивов», то есть массив, элементами которого являются массивы меньшей размерности. С другой стороны, эти массивы могут иметь разную длину.

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

    public static void main (String [] args) {

    int [] [] polyArray = {{11, 12, 13, 14, 15}, {21, 22,

    23}

    {31, 32, 33, 34}};

    for (int [] ir: polyArray) {

    System.out.print (ir.length + «-«) for (int m: ir) {

    System.out.print (m + «»);

    }

    System.out.println ();

    }

    }

    Листинг 3.4.8.1. Вывод двумерного массива Программа выводит:

    5 — 11 12 13 14 15

    3 — 21 22 23

    4 — 31 32 33 34

    Обратите особое внимание на цикл, который осуществляет вывод

    for (int [] ir: polyArray) {

    System.out.print (ir.length + «-«)

    for (int m: ir) {System.out.print (m + «»);

    }

    System.out.println ();

    }

    Этот цикл является двойным, что вполне соответствует интуитивным ожиданиям. Но, если переменная m во внутреннем цикле — это обычная целочисленная переменная, которая пробегает по всем элементам внутреннего массива, то переменная ir во внешнем цикле — это массив, и поэтому имеет тип int [].

    КОНТРОЛЬНЫЕ ВОПРОСЫ К ЧАСТИ 3

    1. Зачем в программе использовать комментарии?
    2. Как записываются однострочные и многострочные комментарии в Java?
    3. Что такое переменная?
    4. Как в программе задать константу?
    5. Охарактеризуйте понятие типа данных.
    6. Какие Вы знаете типы данных в Java?
    7. Тип данных чаще всего используется для описания целых чисел?
    8. Какой тип имеет выражение a == b?
    9. Как выполняется оператор присваивания (предоставление значения)?
    10. Чем отличаются операции x == y и x = y?
    11. Что означает запись c / = 5?
    12. Что такое инкремент и декремент?
    13. Чем отличается преинкремент от постинкременту?
    14. Что такое условный оператор?
    15. Как найти наибольшее из двух чисел с помощью условного оператора?
    16. Как найти наибольшее из двух чисел с помощью тернарной операции?
    17. Перечислите известные Вам типы циклов.
    18. Нарисуйте блок-схему цикла for.
    19. Напишите цикл while, эквивалентный циклу for (int i = 0; i <100; i ++ 0

      {DoSomething (i);}

    20. Каким образом можно найти сумму элементов массива с помощью обычного и модифицированного for?
    21. Для чего используются инструкции break и continue?
    22. Для чего и как записывается оператор варианта?
    23. Что такое функция?
    24. Каким образом функция возвращает результат своего выполнения?
    25. Может тип аргументов функции отличаться от типа ее результата?
    26. Что означает слово void в записи void foo () {}?
    27. Экземпляром какого класса есть строка?
    28. Существующая операция для конкатенации строк?
    29. Каким образом можно получить символ стоит на заданной позиции в строке?
    30. Как сравнить две строки на равенство?
    31. Охарактеризуйте понятие класса и экземпляра класса.
    32. Как в Java описываются классы?
    33. Как создать экземпляр класса?
    34. Что такое конструктор?
    35. Может в классе быть несколько конструкторов?
    36. Которое существует правило относительно полей класса с точки зрения инкапсуляции?
    37. Что такое селекторы и модификаторы?
    38. Что означают описания private и public, если они применяются к полям и методам класса?
    39. Что означает ключевое слово this?
    40. Как в Java записывается наследования?
    41. Какой класс находится на вершине иерархии классов?
    42. Какие Вы знаете методы класса Object?
    43. Охарактеризуйте назначение метода equals ().
    44. Охарактеризуйте назначение метода toString ().
    45. Каким образом можно обеспечить, чтобы для экземпляру ekz некоторого класса можно было написать System.out.println (ekz)?
    46. Каким образом можно обеспечить, чтобы для экземпляру ekz некоторого класса при вызове

      System.out.println (ekz) выводились значения полей этого экземпляра?

    47. Можно создать класс с собственными методами equals () и toString ()?
    48. Для чего предназначена аннотация @Override?
    49. Как можно ускорить написание конструкторов, селекторов и модификаторов при создании класса в Eclipse?
    50. Что такое интерфейс?
    51. Каким образом класс может реализовать некоторый интерфейс?
    52. Что такое перечисляемый тип?
    53. Как описать перечисляемый тип?
    54. Может управляющая переменная в операторе варианта switch быть перечислений типа?
    55. Что такое исключение?
    56. Охарактеризуйте понятие контролируемого и неконтролируемого исключения.
    57. К какому классу исключений относится IOException: контролируемых или неконтролируемых?
    58. Что означает запись throws Exception в описании метода?
    59. Опишите конструкцию try-catch-finally.
    60. Возможна ситуация, когда с секцией try связано несколько секций catch?
    61. Что такое массив?
    62. Как создать массив?
    63. Может массив быть задан литерально?
    64. Каким образом можно получить доступ к элементу массива с заданным номером?
    65. Для чего предназначен класс Arrays?
    66. С помощью какого метода класса Arrays можно вывести на экран все элементы массива?
    67. Как отсортировать массив?
    68. Можно ли при задании размера массива указывать переменные?
    69. С чем связана основное неудобство при использовании массивов?
    70. Опишите коллекцию ArrayList.
    71. Как в коллекцию класса ArrayList добавить новый элемент?

    Часть 4. АНАЛИЗ ЭФФЕКТИВНОСТИ, отладки и тестирования

    Раздел 4.1. отладки программ

        1. Общее представление о отладки

          Программирование — это не только написание кода. Значительная часть работы программиста приходится на отладки программы, то есть на устранение ошибок в ней.

          Написать хоть немного нетривиальную программу так, чтобы она с первого раза успешно компилювалася и правильно работала, практически никогда не удается. Программные ошибки могут быть связаны с самыми разнообразными факторами: недостаточная внимательность или небрежность программиста, недостаточное понимание особенностей языка, ошибки в самом алгоритме и тому подобное. Существует даже шутливое выражение: «в любой программе, работающей, есть по крайней мере одна ошибка».

          К счастью, значительное количество ошибок оказывается еще на этапе компиляции. компилятор

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

            Классическая методика отладки, то есть выявление и устранение программных ошибок

          • отслеживание промежуточных результатов и значений переменных. Эта методика позволяет проанализировать, как меняются значения переменных во время выполнения программы и на основе этого предположения и выводы, правильно ли она работает, а если нет — что именно идет не так.
        2. Простейшие приемы отладки

          Рассмотрим простой пример — цикл, который должен подсчитать сумму первых пяти натуральных чисел. Пусть первая версия этого цикла имеет вид

          package demodebug;

          public class DemoDebug {

          public static void main (String [] args) {

          int s = 0;

          for (int i = 0; i == 5; i ++) {

          s = + i;

          }

          System. out .println ( «Sum is» + s);

          }

          }

          Листинг 4.1.2.1. Начальный неправильный вариант подсчета суммы натуральных чисел

          Результат должен быть равен 15, но программа выводит 0. Что именно не так? Важно делать какие-то разумные предположения. Прежде всего стоит убедиться,

          выполняется цикл вообще. При этом одновременно можно посмотреть, как меняется сумма. Для этого на каждой итерации будем просто выводить значение переменной s:

          public static void main (String [] args) {int s = 0;

          for (int i = 0; i == 5; i ++) {s = + i; System.out.println ( «s =» + s);

          }

          System.out.println ( «Sum is» + s);

          }

          Листинг 4.1.2.2. Первая попытка исправления — вывод промежуточных результатов В результате получаем

          Sum is 0

          В цикле ничего не было выведено — значит, он вообще не работал. Надо выяснить, почему. Здесь приходит в голову посмотреть на заголовок цикла. Действительно, вместо правильной условия продолжения цикла i <5 написано i == 5, а это выражение в начале работы программы является ложным. Поэтому цикл и не выполняется ни разу.

          Исправляем эту ошибку: package demodebug; public class DemoDebug {

          public static void main (String [] args) {int s = 0;

          for (int i = 0; i <5; i ++) {

          s = + i; System.out.println ( «s =» + s);

          }

          System.out.println ( «Sum is» + s);

          }

          }

          Листинг 4.1.2.3. Исправление заголовке цикла Программа выводит:

          s = 0 s = 1 s = 2 s = 3

          s = 4

          Sum is 4

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

          Смотрим на промежуточные результаты — они показывают, что происходит с суммой

          1. Видим, что она меняется не так, как нужно. Выясняем, почему так — для этого смотрим на тело цикла. Так и есть: вместо правильного

            s + = i;

            у нас написано

            s = + i;

            ее.

            есть на каждой итерации очередной плагин не прибавляется к сумме, а полностью заменяет Исправляем:

            package demodebug; public class DemoDebug {

            public static void main (String [] args) {int s = 0;

            for (int i = 0; i <5; i ++) {

            s + = i;

            System.out.println ( «s =» + s);

            }

            System.out.println ( «Sum is» + s);

            }

            }

            Листинг 4.1.2.4. Не та сумма

            Этот вариант программы выводит

            s = 0 s = 1 s = 3 s = 6 s = 10

            Sum is 10

            В принципе, сумма подсчитывается правильно — но не та сумма, которая требуется. Эта ошибка связана уже не из кодированием, а с недостаточным пониманием самой задачи. Первое натуральное число — это 1, а не 0, и поэтому надо считать сумму не от 0 до 4, а от 1 до 5.

            Вносим соответствующие изменения:

            package demodebug; public class DemoDebug {

            public static void main (String [] args) {int s = 0;

            for (int i = 1; i <= 5; i ++) {

            s + = i; System.out.println ( «s =» + s);

            }

            System.out.println ( «Sum is» + s);

            }

            }

            Листинг 4.1.2.5. Подсчет суммы натуральных чисел: исправленный вариант Теперь результат правильный. Программа выводит:

            s = 1 s = 3 s = 6 s = 10 s = 15

            Sum is 15

            Конечно, в этом примере мы имели дело с элементарными ошибками — но ошибки могут быть и более замаскированными …

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

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

        3. Использование протоколирования для отладки

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

          Ряд возможностей для протоколирования предоставляются классом Logger из пакета

          java.util.logging.

          Вернемся к листингу 4.1.2.3; приведем его еще раз:

          package demodebug; public class DemoDebug {

          public static void main (String [] args) {int s = 0;

          for (int i = 0; i <5; i ++) {

          s = + i;

          System.out.println ( «s =» + s);

          }

          System.out.println ( «Sum is» + s);

          }

          }

          Теперь будем осуществлять вывод промежуточных результатов не с помощью «классического» System.out.prinln (), а с помощью класса Logger:

          package demodebug;

          import java.util.logging.Logger; public class DemoDebug {

          public static void main (String [] args) {

          Logger logger = Logger.getLogger (Logger.GLOBAL_LOGGER_NAME)

          int s = 0;

          for (int i = 1; i <= 5; i ++) {

          s + = i; logger.info ( «s =» + s);

          }

          System.out.println ( «Sum is» + s);

          }

          }

          Листинг 4.1.3.1. Вывод промежуточных результатов с помощью класса Logger

          Здесь мы использовали стандартный регистратор протокольных сообщений (Logger.GLOBAL_LOGGER_NAME). Регистратор фактически связывается с идентификатором, который определяет некоторую логическую группу сообщений. Очень легко создать собственный регистратор, такая практика является более рекомендованной.

          -Прежнему выводятся промежуточные результаты; вывода может иметь вид

          червь. 13 2014 10:19:17 AM demodebug.DemoDebug main

          INFO: s = 1

          червь. 13 2014 10:19:17 AM demodebug.DemoDebug main INFO: s = 3

          червь. 13 2014 10:19:17 AM demodebug.DemoDebug main INFO: s = 6

          червь. 13 2014 10:19:17 AM demodebug.DemoDebug main INFO: s = 10

          червь. 13 2014 10:19:17 AM demodebug.DemoDebug main INFO: s = 15

          Sum is 15

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

          logger.setLevel (Level.OFF)

          Программа будет выглядеть:

          package demodebug;

          import java.util.logging.Level; import java.util.logging.Logger;

          public class DemoDebug {

          public static void main (String [] args) {

          Logger logger = Logger.getLogger (Logger.GLOBAL_LOGGER_NAME)

          logger.setLevel (Level.OFF)

          int s = 0;

          for (int i = 1; i <= 5; i ++) {

          s + = i; logger.info ( «s =» + s);

          }

          System.out.println ( «Sum is» + s);

          }

          }

          Листинг 4.1.3.2. Отключение протокольных сообщений

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

        4. Инструментальные средства отладки

    Интегрированные среды разработки, в частности Eclipse или NetBeans, предоставляют в распоряжение очень удобные средства отладки. Посмотрим, как можно осуществлять отладки в Eclipse.

    Обычным образом подготовим в Eclipse следующий код:

    package demodebug;

    public class DemoDebug {

    public static void main (String [] args) {int s = 0;

    for (int i = 0; i <5; i ++) {

    s = + i;

    }

    System.out.println ( «Sum is» + s);

    }

    }

    Листинг 4.1.4.1. Программа для демонстрации возможностей Eclipse для отладки

    Обратите внимание: вывод промежуточных результатов здесь нет. Среда имеет выглядеть, подобный следующему:

    image

    Начнем отладки. Для этого должна быть установлена перспектива Debug.Нужно также установить точку остановки в начале программы (это можно сделать с помощью опции Toggle Breakpoint контекстного меню).

    Запускаем программу в режиме отладки (пункт меню Run => Debug или соответствующая пиктограмма). Программа остановится на установленной нами точке остановки. В отдельном окне можно увидеть существующие на данный момент переменные (в начале программы это массив аргументов командной строки).

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

    image

    Далее продолжаем выполнение в пошаговом режиме с помощью пунктов меню Run => Step Into / Step Over или соответствующих пиктограмм. После каждого шага состояние переменных может меняться, и можно отслеживать эти изменения. Например, на определенной итерации цикла среда может иметь вид

    image

    Мы видим, что в данный момент переменная s имеет значение 2, а переменная i — значение 3. Аналогичным образом можно отслеживать поля экземпляров классов, коллекции и т.п.

    Раздел 4.2. тестирование программ

        1. Роль тестирования в программном проекте

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

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

          Типичная схема модульного тестирования заключается в проверке работы модуля на основе заранее подобранных тестов — наборов данных, для которых известны правильные результаты. Расхождение между ожидаемыми и фактически полученными результатами сигнализирует о наличии ошибки. Ключевым результатов не доказывает правильности программы, но повышает степень уверенности в отсутствии ошибок.

          Чем полнее и более представительным является набор тестов, тем больше оснований доверять программе. Тестовые примеры должны покрывать, во-первых, возможный диапазон входных данных, а при возможности — варианты работы программы в соответствии с ее логики.

          В ряде методик разработки программного обеспечения тестирования вообще играет ключевую роль. Например, TDD (Test-Driven Development, разработка на основе тестов) предусматривает сначала написания тестов, а уже потом реального кода.

          Очевидно подходом к модульного тестирования является простое вывода на экран фактических результатов работы программы наряду с правильными. Но просмотр вывода с целью сопоставления результатов не особенно удобным. Кроме того, в коде появляется много операций вывода, которые затем станут ненужными. Хорошей идеей было бы, во-первых, отделить основной рабочий код от запуска тестов, а во-вторых — каким образом автоматизировать сам процесс проверки. Существует ряд утилит, предназначенных для автоматизации

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

        2. Знакомство с JUnit

    Проиллюстрируем работу этой утилиты на очень простом примере, а именно — на примере создания функции, которая находит произведение двух целых чисел. На этом игрушечном примере мы Промоделируем ситуацию, будто мы заранее не знаем, как это делается. Возьмем такие тестовые данные:

    Вход: 0 0; результат — 0.

    Вход: 1 1: результат — 1.

    Вход 2 2, результат — 4.

    Вход 3 4, результат — 12.

    Создадим в среде Eclipse новый проект. Создадим в этом проекте класс, в котором напишем заглушку функции:

    package multnumbers; public class MultNumbers {

    public byte multNumbers (byte a, byte b) {return 0;

    }

    }

    Листинг 4.2.2.1. Функция вычисления произведения: первоначальный вариант Сначала эта функция всегда возвращает 0.

    Теперь подготовим средства для тестирования. Для простоты разместим их в этом же

    пакете. Кликните правой кнопкой мыши по названию пакета и выберем New-> Other-

    > JUnit-> JUnit Test Case. Должно появиться следующее диалоговое окно:

    image

    В поле Name зададим название тестового класса (назовем его NumberTest), а в поле

    Class under test — название класса, мы тестируем (это MultNumbers).

    Далее будет предложено выбрать методы, которые мы будем тестировать, и для которых нужно создать тестовые заглушки:

    image

    Заметим метод multNumbers (byte, byte).

    Далее нам может быть предложено подключить библиотеку JUnit (если это не было сделано ранее). Согласимся с этим предложением.

    В результате должен быть создан новый тестовый класс:

    package multnumbers;

    import static org.junit.Assert. *; import org.junit.Test;

    public class NumberTest {

    @Test

    public void testMultNumbers () {fail ( «Not yet implemented»)

    }

    }

    Листинг 4.2.2.2. Класс для модульного тестирования, сгенерированный JUnit

    Мы видим заготовку тестового метода testMultNumbers (), который нужно наполнить конкретным содержанием. Она сопровождается аннотацией @Test.

    В этом методе мы можем осуществить проверку сразу всех тестовых примеров, но

    тогда трудно будет отделить, для каких из них результаты были правильные, а для каких — нет. Поэтому сделаем для каждого тестового примера отдельные методы.

    Основной инструмент контроля, мы будем использовать — это метод Assert.assertTrue (condition).Если условие condition является ошибочной, он выбрасывает исключение.

    Как подготовительные действия, надо создать экземпляр класса MultNumbers. Один из тестов может иметь вид:

    @Test

    public void testMultNumbers00 () {Assert.assertTrue (mn.multNumbers ((byte) 0, (byte)

    0) == 0);

    }

    Напишем другие тестовые методы. Весь тестовый класс станет таким:

    package multnumbers;

    import static org.junit.Assert. *; import org.junit.Assert;

    import org.junit.Test;

    public class NumberTest {

    // Вход: 0 0; результат — 0.

    // Вход: 1 1: результат — 1.

    // Вход 2 2, результат — 4.

    // Вход 3 4, результат — 12.

    MultNumbers mn = new MultNumbers ();

    @Test

    public void testMultNumbers00 () {Assert.assertTrue (mn.multNumbers ((byte) 0, (byte)

    0) == 0);

    }

    @Test

    public void testMultNumbers11 () {Assert.assertTrue (mn.multNumbers ((byte) 1, (byte)

    1) == 1)

    }

    @Test

    public void testMultNumbers22 () {Assert.assertTrue (mn.multNumbers ((byte) 2, (byte)

    2) == 4)

    }

    @Test

    public void testMultNumbers34 () {Assert.assertTrue (mn.multNumbers ((byte) 3, (byte)

    4) == 12);

    }

    }

    Листинг 4.2.2.3. Скорректированный тестовый класс, в котором различные тестовые примеры разделены

    Теперь можно запустить тест так же, как мы запускали обычное применение (зеленый треугольник или пункт меню Run -> Run). Должно появиться следующее окошко:

    image

    Красная полоска свидетельствует о том, что тест провалился. Слева мы можем видеть, на каких именно тестовых примерах были ошибки. В данном случае правильные результаты были получены только для входящих данных (0, 0) — напомним, что вместо настоящих вычислений у нас пока есть заглушка, которая всегда возвращает 0.

    Будем изменять код функции. Попробуем следующий вариант:

    public byte multNumbers (byte a, byte b) {

    return (byte) (a + b)

    }

    Листинг 4.2.2.4. Тест для этой функции проваливается: ложная операция

    Еще раз запустим тест. Теперь успешны два тестовых примеров. Попробуем еще один вариант вычислений:

    public byte multNumbers (byte a, byte b) {

    return (byte) (a * b)

    }

    image

    Листинг 4.2.2.5. Для этой функции тест успешный Запускаем тест.

    Ура! Зеленая полоска свидетельствует об успешности теста: ошибок на тестовых примерах не было. Но доказывает это правильность программы?

    Как общее правило — нет, не обязательно. В данном случае сам набор тестовых примеров был недостаточно представительным: все они были рассчитаны только на маленькие числа. Добавим еще один пример: попробуем умножить 10 на 30:

    @Test

    public void testMultNumbers1030 () {Assert.assertTrue (mn.multNumbers ((byte) 10 (byte)

    30) == 300)

    }

    Листинг 4.2.2.6. Еще один тестовый пример; на нем тест проваливается На этом примере тест проваливается.

    Попробуем вместо типа byte работать с типом int. Меняем код функции:

    public int multNumbers (int a, int b) {return a * b;

    }

    Листинг 4.2.2.7. Скорректированная функция умножения; тест успешный

    Вместе с этим придется убрать приведения типов в тестовых примерах.

    Получаем успешное прохождение теста. Ура …

    Остается вопрос: а что будет, если значения операндов или результат умножения выйдут за рамки типа int (или даже long)? Но это уже несколько другой вопрос …

    Раздел 4.3. Качество и эффективность программ

        1. Понятие о качестве программного обеспечения

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

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

            • эффективность .- степень использования системных ресурсов.Программа по крайней мере должна выдавать результаты за приемлемое время и не быть слишком ресурсоемкой;

            • надежность — способность к выполнению необходимых функций в определенных условиях, устойчивость к сбоям;

            • удобство в использовании.Программа должна быть удобной в усвоении и использовании, а ее интерфейс — интуитивно понятный;

            • адаптированность — способность к использованию программы без изменений в условиях, на которые она не была рассчитана непосредственно;

            • удобство сопровождения — возможность легкого внесения изменений для расширения возможностей программы, исправления дефектов и тому подобное;

            • гибкость — легкость изменения кода программы для использования в условиях, на которые она не была непосредственно рассчитана;

            • возможность повторного использования — легкость использования отдельных частей программы в других программах и средах;

            • читабельность — легкость чтения кода.

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

        2. Основные показатели эффективности

          Эффективность программы определяется на основе следующих критериев:

            • время выполнения программы, быстродействие;

            • количество памяти, необходимая для выполнения программы;

            • использование других системных ресурсов.

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

          Для количественного измерения эффективности существует много средств мониторинга и профилирования — в частности, профилировщики, которые включены в состав IDE или могут быть подключены к ним как плагины. Но существуют простые и общеупотребительные средства измерения эффективности отдельных фрагментов программы прямо в ее коде.

        3. Исследование времени выполнения фрагментов кода

          Самый простой способ определить время выполнения того или иного фрагмента программы

          • воспользоваться функцией System.nanotime ().Эта функция возвращает количество наносекунд, которая прошла с некоторого случайного момента времени.

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

            long t1 = System.nanoTime ();

            фиксируется одна временная метка, а сразу же после завершения этого фрагмента — вторая.

            Тогда разница между этими фрагментами и будет время выполнения кода, исследуется.

            Часто удобнее выводить не количество наносекунд, а количество более крупных отрезков времени — микросекунд или миллисекунд.

            Описанный подход можно проиллюстрировать следующим кодом:

            long t1 = System.nanoTime ();

            // Код, который исследуется long t2 = System.nanoTime (); long elapsedTime = t2-t1;

            System.out.println ( «Elapsed time was» + elapsedTime + «ns»)

            Листинг 4.3.3.1. Хронометрирования с помощью System.nanoTime ()

            Например определим время, необходимое для вычисления суммы некоторого количества членов гармонического ряда:

            import java.util.Scanner; public class DemoGarmonic {

            public static void main (String [] args) {System.out.println ( «Enter the number of members: ‘); Scanner sc = new Scanner (System.in)

            final int n = sc.nextInt (); double s = 0;

            long t1 = System.nanoTime ();

            for (int i = 1; i <= n; i ++) {s + = 1 / i;

            }

            long t2 = System.nanoTime (); long elapsedTime = (t2-t1) / 1000;

            System.out.printf ( «Sum of% d members of harmonic

            series is% 7.3f \ n «, n, s);

            System.out.println ( «Elapsed time was» + elapsedTime + «

            mks «)

            }

            }

            Листинг 4.3.3.2. Измерение времени вычисления членов гармонического ряда

            Если введенное количество членов равна 100, программа может вывести следующие результаты:

            Sum of 100 members of harmonic series is 5,187 Elapsed time was 43 mks

            С аналогичной целью часто используется System.currentTimeMillis ().В отличие от System.nanoTime (), эта функция возвращает количество миллисекунд, прошедших с полуночи 1 января 1970

            В последнее время приобрел популярность класс StopWatch, для использования которого нужно подключить библиотеку Spring.Framework (пакет org.springframework.util). Соответствующий код может иметь вид:

            StopWatch sw = new StopWatch (); sw.start ();

            // Estimated code sw.stop ();

            System.out.println ( «Elapsed time:» + sw.getLastTaskTimeMillis () + «ms»)

            Для начала отсчета времени используется функция start () для завершения — stop ().Функция getLastTaskTimeMillis ()) возвращает время, прошедшее. Функции shortSummary () и prettyPrint () возвращают более полное описание задачи, которая выполнялась.

            Проиллюстрируем использование StopWatch для задачи о сумме гармонического ряда.

            Программа может быть такой:

            package demostopwatch; import java.util.Scanner;

            import org.springframework.util.StopWatch;

            public class Demostopwatch {

            public static void main (String [] args) {System.out.println ( «Enter the number of members: ‘); Scanner sc = new Scanner (System.in)

            final int n = sc.nextInt (); double s = 0;

            StopWatch sw = new StopWatch (); sw.start ( «Harmonic Series») for (int i = 1; i <= n; i ++) {

            s + = 1 / i;

            }

            sw.stop ();

            System.out.printf ( «Sum of% d members of harmonic series is% 7.3f \ n», n, s);

            System.out.println ( «Elapsed time» + sw.getLastTaskTimeMillis ());

            System.out.println (sw.shortSummary ()); System.out.println ( «*******») System.out.println (sw.prettyPrint ());

            }

            }

            Листинг 4.3.3.3. Использование StopWatch

            При n = 10000 вывода может иметь вид:

            Sum of 10000 members of harmonic series is 9,788 Elapsed time 3

            StopWatch »: running time (millis) = 3

            *******

            StopWatch »: running time (millis) = 3

            ——————————————

            ms% Task name

            ——————————————

            00003 100% Harmonic Series

        4. Пример: сравнение двух способов получения строки из примитивного типа Как получить строку с переменной примитивного типа?Например, если у нас есть переменная

          int a = 2;

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

            • операция +: «» + a;
            • вызов функции String.valueOf (a) это статическая функция класса String. Сравним эти два способа по быстродействию с использованием описанной выше методики.

          Для этого будем создавать строку на каждой итерации достаточно длинного цикла.

          1-й способ.Соответствующая программа имеет вид

          public static void main (String [] args) {final int N = 100000;

          int a = 2;

          long t1 = System.nanoTime ();

          for (int i = 0; i <N; i ++) {String q = » + a;

          }

          mks «)

          }

          long t2 = System.nanoTime (); System.out.println ( «Elapsed time:» + (t2-t1) / 1000 + «

          Листинг 4.3.4.1. Исследование времени получения строки с переменной примитивного типа на основе операции +

          Различные запуски давали разное время выполнения цикла; он колебался в диапазоне 70 000 — 100 000 мкс. Но, ежели вместо переменной мы брали литерально примитивное значение:

          public static void main (String [] args) {final int N = 100000;

          long t1 = System.nanoTime ();

          for (int i = 0; i <N; i ++) {String q = «» + 2;

          mks «)

          }

          }

          long t2 = System.nanoTime (); System.out.println ( «Elapsed time:» + (t2-t1) / 1000 + «

          Листинг 4.3.4.2. Исследование времени получения строки с ЛИТЕРАЛЬ примитивного типа на основи.операции +

          время выполнения был значительно меньше и колебался в диапазоне 3000-6000 мкс.

          Способ 2.Теперь будем получить строку с переменной с помощью

          String.valueOf ().

          public static void main (String [] args) {final int N = 100000;

          int a = 2;

          long t1 = System.nanoTime (); for (int i = 0; i <N; i ++) {

          String q = String.valueOf (a)

          mks «)

          }

          }

          long t2 = System.nanoTime (); System.out.println ( «Elapsed time:» + (t2-t1) / 1000 + «

          Листинг 4.3.4.3. Исследование времени получения строки с переменной примитивного типа на основе String.valueOf ()

          Время выполнения цикла составлял 30000 — 40000 мкс, то есть меньше, чем аналогичного цикла при использовании операции +. Но теперь замена переменной на литерал практически не изменила ситуации.

        5. Пример: сравнение интерпретации и синхронной компиляции

          Мы уже говорили, что JIT-компиляция (или синхронная компиляция), которая осуществляется виртуальной машиной, позволяет достичь существенного сокращения времени выполнения программ по сравнению с традиционной интерпретацией. Попробуем применить технику хронометрирования программ, описанную в предыдущем разделе, для экспериментального исследования этого вопроса.

          Схема эксперимента заключается в следующем.

          1. Напишем более или менее сложный цикл, желательно достаточно сложный. Обеспечим хронометраж программы (или самого цикла).
          2. Компилируем программу.
          3. Выполняем программу обычным образом (это будет комбинированный режим с переходом к синхронной компиляции).
          4. Выполняем программу в режиме чистой интерпретации:

            java -Xint класс

          5. Сравниваем результаты.

            Эксперимент 1.

            Сравнительно простой цикл (но запутанными выражениями):

            public class explorecycle {

            public static void main (String [] args) {

            int q;

            int REPEATS = 100000;

            long t1; long t2;

            double elapsed;

            int s = 0;

            q = 1;

            q = q + i; q = qi;

            t1 = System.nanoTime ();

            for (int i = 0; i <REPEATS; i ++) {

            s = (i + 1) * (q + ii) * (q + q * iq * i)

            }

            t2 = System.nanoTime ();

            elapsed = ((double) t2-t1) / 1000.;

            System.out.println ( «Elapsed time is» + elapsed + «

            microsecondss «)

            }

            }

            System.out.println ( «Result is» + s);

            Листинг 4.3.5.1. Исследование эффективности и синхронной компиляции: некоторое надуманный цикл

            Было проведено три эксперимента (время дано в микросекундах)

            JIT-компиляция

            интерпретация

            1

            11281

            37476

            2

            11475

            42976

            3

            11228

            37581

            Как видим, достигается ускорение примерно в 3,5 раза.

            эксперимент 2

            Для того же кода уменьшим количество итераций цикла (возьмем REPEATS = 1000).

            JIT-компиляция

            интерпретация

            1

            391,7

            421,2

            2

            421,8

            379,9

            3

            378,7

            376,3

            Мы видим, что интерпретация может работать даже быстрее, чем синхронная компиляция. Это объяснимо: сначала виртуальная машина все равно работает в режиме интерпретации. Для перехода в режим JIT-компиляции ей нужно набрать статистику, а это связано с определенными накладными расходами.

        6. Понятие о Java Code Conventions

    Документ Java Code Conventions, который можно скачать с сайта Oracle, содержит ряд «канонических» требований к Java-кода. Все программисты, которые пишут программы на Java, должны знать эти требования и соблюдать их.

    Многие рекомендации Java Code Conventions носят общий характер. В качестве примера приведем сакраментальные призывы о необходимости использования комментариев или о том, что названия переменных, функций и т.д. должны быть осмысленными и отображать их назначения. Пренебрежение этим принципом резко снижает читабельность программы. Действительно, следующая функция для вычисления пройденного пути по известным скоростью и временем движения

    duuble calculateDistance (double speed, double time) {return speed * time;

    }

    выглядит гораздо лучше, чем функция double a (double b, double c) {return b * c;

    }

    Кроме того, Java Code Conventions содержит ряд требований, специфических для Java.

    Перечислим некоторые из них.

    Имена функций должны начинаться с маленькой буквы — calculate (), а не Calculate (). Если же имя является сложным и состоит из нескольких слов, первое слово должно начинаться с маленькой буквы, а все последующие — с большой (так называемый camel-стиль): например, calculateAnnualIncome ().Те же правила касаются имен переменных.

    Для имен функций лучше использовать глаголы. Так, имя makeReport () является лучшей названием для функции, чем myReport ().

    Названия классов следует начинать с большой буквы (например, Entity). Если же название класса состоит из нескольких слов, с большой буквы должно начинаться каждое слово (например, MyWonderfulEntity).

    Имена констант следует писать большими буквами. Например:

    final int NAME = «Bob»;

    КОНТРОЛЬНЫЕ ВОПРОСЫ

    1. Охарактеризуйте понятие отладки программ.
    2. Какие Вы знаете способы отладки программ.
    3. Какие Вы знаете недостатки обычное вывода промежуточных результатов с целью отладки?
    4. Как можно отключить промежуточное вывода при протоколировании с использованием

      java.util.logging?

    5. Каким образом можно отслеживать ход выполнения программы и изменение переменных в среде Eclipse?
    6. Охарактетизуйте понятия тестирования программ.
    7. Что такое модульное тестирование?
    8. Охарактеризуйте понятие Test Driven Development.
    9. Опишите известные Вам подходы к осуществлению тестирования.
    10. Что такое тестовые примеры?
    11. Всегда успешное прохождение всех тестов доказывает правильность программы?
    12. Опишите использование JUnit для тестирования.
    13. Какие Вы знаете требования к программному обеспечению?
    14. Перечислите основные показатели, которые определяют эффективность программ.
    15. Опишите, как можно использовать функцию System.nanoTime для измерения времени выполнения программ.
    16. Перечислите основные требования Java Code Conventions относительно названий классов, методов, переменных и констант.

    Упражнения на программирование С JAVA

    Простейшие консольные приложения

    Hello, World! Написать консольное приложение, которое выводит на экран некоторый текст (например, Hello, world!). Упражнение выполнить в двух вариантах:

  • текст выводится должно быть литералом в операции вывода;
  • текст выводится должно быть полем основного (и единственного) класса.

    Приветствие. Написать консольное приложение, которое вводит с клавиатуры имя пользователя и выводит на экран приветствия этом пользователю. Введение выполнить в двух вариантах:

  • с использованием класса BufferedReader;
  • с использованием класса Scanner;
  • с использованием класса JOptionPane

    Работа с числами.Написать консольное приложение, которое вводит с клавиатуры действительное число и выводит на экран двоичный логарифм этого числа. Введение выполнить в двух вариантах:

  • с использованием класса BufferedReader;
  • с использованием класса Scanner. Вывод тоже выполнить в двух вариантах:
  • в System.out.println использовать строчную конкатенацию;
  • использовать отформатировано вывода (функцию System.out.printf). На примере вычисления логарифмов проиллюстрировать статический импорт.

    Аргументированные команды. Написать консольное приложение, которое подсчитывает сумму действительных чисел, заданных в качестве аргументов командной строки. Программа должна контролировать корректность ввода и в случае, если аргументов командной строки нет или они имеют неправильный формат, выдавать соответствующее сообщение. Программу запускать как в командной строке, так и средствами IDE.

    Пароли из консоли. Написать консольное приложение, которое с использованием класса Console вводит логин и пароль и, если они правильные, выводит You are welcome, а в противном случае — Access denied.

    Сумма натуральных чисел. Написать функцию, которая находит сумму первых n натуральных чисел; число n является параметром функции.

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

    n

    n

    i 1

    i i 1

    число n> 0 должно быть параметром функции.

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

    i — увеличить значение переменной на 1, d — уменьшить значение на 1,

    u — восстановить значения по умолчанию (пусть это значение будет равно 10); q — завершить выполнение программы.

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

    Рекордные строки. Написать консольное приложение, которое вводит с клавиатуры некоторое количество строк и выводит на экран самый длинный и самый короткий из них, а также длины этих строк.

    Основные типы данных и операции с ними

    Двоичное представление. Написате функцию / функции, которые позволяют получить внутреннее двоичное представление для переменных всех примитивных типов, кроме boolean.

    Сумма цифр. Написать консольное приложение, которое вводит с клавиатуры целое неотрицательное число, количество разрядов которого гарантированно не превышает 200, и выводит на экран сумму его цифр.

    Длинные цели. Написать консольное приложение, которое вводит с клавиатуры два больших целых числа и находит их сумму. Использовать класс BigInteger.

    Вычисление факториалов. Вывести на экран таблицу значения факториалов n!от 1 до максимального целого числа, для которого факториал вычисляется точно. Для контроля точности использовать класс BigInteger.Сравнить результаты вычислений для случаев, когда типом результата является int, long и double. Объяснить, почему использование типа double не дает существенного выиграше по сравнению с типом long.

    .

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

    Основные алгоритмические конструкции

    Числа Фибоначчи.Написать функцию, которая принимает целое число и возвращает число Фибоначчи с этим номером. Последовательность чисел Фибоначчи исчисляется следующим образом: первые два члена равны 1, а каждое следующее равно сумме двух предыдущих. Упражнение выполнить в двух вариантах:

  • нерекурсивный;
  • рекурсивный.

    Исследовать время выполнения программы. Позаботиться о максимально возможное снижение временных затрат (особенно для рекурсивного варианта).

    Наш синус.Написать функцию, которая вычисляет синус действительного аргумента на основе разложения в ряд Тейлора. Стандартные функции использовать только для тестирования работы функции.

    Половинное деление. Написать функцию, которая находит на отрезке [a, b] корень уравнения f (x) = 0 методом половинного деления. Считается, что функция f (x) гарантированно есть или монотонно возрастающей, или монотонно убывающей, и имеет единый корень на указанном отрезке .. Концы отрезка и точность развязку; связи должны быть параметрами функции.

    Решето Эратосфена. Рассматривается задача: на основе решета Эратосфена найти все простые числа, не превышающие заданного числа, и выводит их на экран; при этом используются массивы. Дан такой вариант программы:

    import java.util.Arrays; public class Eratosfen {

    public static void main (String [] args) {final int n = 100;

    boolean [] flags = new boolean [n + 1]; Arrays.fill (flags, false);

    int curr = 2;

    while (curr <Math.sqrt (n)) {

    for (int k = 2; k <= (n / curr) k ++) {flags [curr * k] = true;

    }

    curr ++;

    }

    for (int i = 2; i <= n; i ++) {if (!flags [i])

    System.out.print (i + «»);

    }

    System.out.println ( «\ n»);

    }

    }

    Исследовать время выполнения основного цикла. Модифицировать программу так, чтобы минимизировать временные затраты на его выполнение

    Быстрый подъем. Написать функцию, которая преподносит натуральное число к целому степени n, осуществляя при этом существенно меньше чем n умножений. Использовать метод инвариантов.

    Геометрическая прогрессия. Написать программу, которая вводит с клавиатуры n целых чисел, проверяет, образуют они геометрическую прогрессию, и если да — выводит на экран коэффициент прогрессии. Число n вводится с клавиатуры.

    Золотая середина. Написать функцию, которая принимает в качестве аргументов три целых числа и возвращает среднее из них. Например, для аргументов 25, 2, 30 она должна вернуть 25.

    Массивы и строки

    Последовательность целых. Написать программу, которая формирует последовательность целых положительных чисел, которые вводятся с клавиатуры. Количество чисел заранее неизвестна; признаком конца последовательностью является введение 0. Если вводится отрицательное число или что-то, что не является целым числом, программа имеет попросить ввести правильные данные ..

    Упражнение выполнить в двух вариантах:

  • с использованием массивов;
  • с использованием ArrayList.

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

Многомерный массив. Написать функцию, которая принимает переменное количество строк и возвращает многомерный массив, элементами которого в свою очередь являются массивы типа int [], состоящие из цифр каждой строки и отсортированы по убыванию. Например, при входных данных «q2r14gf», «5w9» функция должна вернуть массив [[4,2,1], [9,5]].Проиллюстрировать работу функции в консольном приложении.

Массивы — геть! Переделать упражнение «Многомерный массив» так, чтобы функция возвращала НЕ многомерный массив, а коллекцию соответствующего типа.

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

int [] [] arr = {

{11, 12, 13},

{21, 22};

{31}, {32}, {33}, {34}

};

соответствует таблица

11

12

13

21

22

31

32

33

34

Напишите функцию, которая принимает в качестве аргумента двоовимирний массив и возвращает коллекцию ArrayList, каждый элемент которого равен количеству заполненных ячеек в соответствующем столбце таблицы. Например, в нашем случае функция должна вернуть коллекцию {3, 3, 2, 1}

Системы уравнений. Написать функцию, которая решает систему линейных алгебраических уравнений; функция должна принимать в качестве аргументов двумерный массив коэффициентов типа double [] [] и одномерный массив свободных членов типа double [] и возвращать решение в виде одномерного массива действительных чисел.

Палиндром. Написать программу, которая вводит с клавиатуры строку и проверяет, есть ли он палиндромом, то есть читается он все равно слева направо и справа налево.

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

qwerty функция должна вернуть ywtqre.Проверить работу написанной функции в основной программе.

пакеты

Работа с пакетами. Создать пакет mycompany.training. <Фамилия>, к которому включить несколько произвольных классов. Упаковать пакет в jar-архива. Написать консольное приложение, которое иллюстрирует использование как неупакованного пакета, так и упакованного jar- файла.

Основные приемы ООП

Канонический класс. Написать класс MyClass с полем a типа int. С каждым экземпляром класса имеет связываться его порядковый номер. С этой целью в классе нужно предусмотреть поле номер, а в конструкторе — установление номера.

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

Создать коллекцию ArrayList, которая должна состоять из нескольких экземпляров этого класса, и вывести ее содержимое на экран. Каждый экземпляр имеет выводиться в виде: номер — a.

Перечень листингов программ

1.2.3.1. Hello, world: даже самая простая программа требует создания классов

2.1.3.1. Простая программа — компиляция и выполнение

        1. Элементарная программа для дизассемблирование
        2. Результат дизассемблирование программы из листинга 2.1.8.1
        1. Основной класс консольного приложения
        2. Класс не является публичным
        1. Самый простой вариант Hello, world: вывод литералов.
        2. Вывод строчной переменной.
        3. Вывод константы.
        4. Вывод поля класса: неправильный вариант
        5. Вывод статического поля: правильно
        1. Использование аргументов командной строки
        2. Проверка правильности передачи аргументов командной строки

2.2.6.1. Программа без метода main

        1. Ввод символа: характерная ошибка компиляции
        2. Ввод символа и не совсем правильное его вывода
        3. Ввода и вывода символа: исправленный вариант

2.3.4.1. Введение строк с помощью класса BufferedReader

2.3.6.1. ввод чисел

        1. Класс Scanner: введение строки
        2. Класс Scanner: введение целого числа
        3. Использование Scanner для расщепления строки; количество слов известна заранее
        4. Использование Scanner для расщепления строки; количество слов неизвестна

2.3.10.1. Неправильное введение целых чисел и строк

2.3.10.1. Введение целых чисел и строк: исправленный вариант

2.3.11.1. Введение с помощью диалогового окна

2.3.12.1. Класс Console: введение имен и паролей

3.В.1. Пример использования классов — вывода текущей даты

3.1.1.1. Использование комментариев

3.1.2.1. Использование переменных — вычисление площади прямоугольника по его сторонами

        1. Вывод символа и его кода
        2. Пример булевых переменных
        1. Нахождение максимума двух чисел с помощью условного оператора
        2. Нахождение максимума двух чисел с помощью готовых функций

3.1.11.1. Нахождение максимума двух чисел с использованием тернарной операции

        1. Нахождение суммы элементов массива с помощью цикла с предусловием
        2. Нахождение суммы элементов массива с помощью цикла с постусловием
        3. Нахождение суммы элементов массива с помощью цикла for
        4. Нахождение суммы элементов массива с помощью модифицированного for
        5. Модифицированный for — характерная ошибка
        1. Задача «Произведение дробей»: неправильное решение
        2. Задача «Произведение дробей»: решение формально правильное, но неэффективное
        3. Задача «Произведение дробей»: исправленный вариант

3.1.14.1. Интерактивное управление — иллюстрация оператора варианта

        1. Функция, которая вычисляет квадрат действительного числа
        2. Иллюстрация использования функций
        1. Вывод первого символа строки: характерная ошибка
        2. Вывод первого символа строки: исправленный вариант

          for

        3. Сравнение строк с помощью ==
        4. Сравнение строк с помощью метода equals

3.2.1.1. простой класс

3.2.1. 2. Демонстрационная программа, которая использует класс

        1. Класс по селекторами и модификаторами
        2. Поля закрыты — ошибка компиляции
        3. Для получения значений полей используем селекторы

3.2.3.1. Типичный вид конструктора с параметрами

        1. Наследование методов подклассов
        2. Переопределения методов в подклассе
        1. Суперкласс с методом main
        2. Класс, который наследуют от суперкласса с методом main ()

3.2.7.1. Элементарный класс для демонстрации методов equals () и toString ()

        1. Проверка равенства объектов: использование оператора ==
        2. Проверка равенства объектов: использование функции equals ()
        3. Переопределение метода equals
        1. Попытка вывести на экран экземпляр класса
        2. Переопределения метода toString ()
        1. Автоматическая генерация элементов класса: первый шаг
        2. Сгенерированный код метода equals ()
        3. Окончательный код класса с автоматически сгенерированными основными методами
        1. Интерфейс с одним методом
        2. Неправильная имплементация интерфейса: необходимый метод не реализован
        3. Правильная имплементация интерфейса
        1. Пример перечислений типа
        2. Создание и использование перечисляемых типов
        1. Ошибка компиляции: отсутствие обработки контролируемых исключений
        2. Использование ключевого слова throws
        3. Обработка исключений в блоке try-catch
        4. Введение числа: много возможных исключений
        1. Низкоуровневая реализация списка на С ++ с использованием указателей
        2. Низкоуровневая реализация списка на Java

3.4.2.1. Литерально задания и перебор массива

3.4.3. 1. Пример сортировки массива

3.4.3.2. Сортировка массива строк по использованию ламбда-функций

3.4.4.1. Массив формируется случайным образом

        1. Экземпляры этого класса будут элементами массива
        2. Программа, которая работает с массивом экземпляров

3.4.6.1. Заполнение и вывод массива: неправильное использование модифицированного

3.4.6.2.Заполнение и вывод массива: неправильный размер массива

3.4.6.3. Заполнение и вывод массива: исправленный вариант

3.4.7.1. Заполнение и вывод коллекции

3.4.8.1. Вывод двумерного массива

        1. Начальный неправильный вариант подсчета суммы натуральных чисел
        2. Первая попытка исправления — вывод промежуточных результатов
        3. Исправление заголовке цикла
        4. Не та сумма
        5. Подсчет суммы натуральных чисел: исправленный вариант
        1. Вывод промежуточных результатов с помощью класса Logger
        2. Отключение протокольных сообщений

4.1.4.1. Программа для демонстрации возможностей Eclipse для отладки

        1. Функция вычисления произведения: первоначальный вариант
        2. Класс для модульного тестирования, сгенерированный JUnit

4.4.2.3. Скорректированный тестовый класс, в котором различные тестовые примеры разделены

        1. Тест для этой функции проваливается: ложная операция
        2. Для этой функции тест успешный
        3. Еще один тестовый пример; на нем тест проваливается
        4. Скорректированная функция умножения; тест успешный
        1. Хронометрирования с помощью System.nanoTime ()
        2. Измерение времени вычисления членов гармонического ряда
        3. Использование StopWatch
        1. Исследование времени получения строки с переменной примитивного типа на основе операции +
        2. Исследование времени получения строки с ЛИТЕРАЛЬ примитивного типа на основи.операции +
        3. Исследование времени получения строки с переменной примитивного типа на основе String.valueOf ()

4.3.5.1. Исследование эффективности и синхронной компиляции: некоторое надуманный цикл

[Всего голосов: 3    Средний: 5/5]

Читать  Лекции по дисциплине "Архитектура компьютерных систем"