автордың кітабын онлайн тегін оқу Java: быстрый старт
Переводчик Е. Матвеев
Литературный редактор М. Петруненко
Художник В. Мостипан
Корректоры М. Одинокова, Н. Сулейманова
Джейми Чан
Java: быстрый старт. — СПб.: Питер, 2021.
ISBN 978-5-4461-1801-4
© ООО Издательство "Питер", 2021
Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме без письменного разрешения владельцев авторских прав.
Предисловие
Эта книга была написана для того, чтобы помочь вам БЫСТРО изучить Java — и изучить ХОРОШО.
Книга не требует от читателя опыта программирования. Даже стопроцентный новичок обнаружит, что в ней просто объясняются сложные концепции. Если вы — опытный разработчик, переходящий на Java, материал обладает достаточной глубиной, чтобы вы могли немедленно взяться за программирование.
Чтобы дать вам широкое представление о Java, не перегружая лишней информацией, я тщательно подошел к выбору тем. В частности, рассматриваются концепции объектно-ориентированного программирования, средства обработки ошибок, работа с файлами и т.д. Также в книге нашлось место для лямбда-выражений. Примеры были подобраны для того, чтобы продемонстрировать каждую концепцию и дать читателю более глубокое понимание языка.
Как сказал Ричард Брэнсон: «Самый лучший способ чему-либо научиться — начать это делать». Книга завершается проектом, в котором вы создадите программу учета посетителей клубов с нуля. В проекте используются концепции, изложенные в книге, и вы сможете увидеть, как они складываются в единое целое.
Исходный код проекта и всех примеров книги доступен по адресу: https://www.learncodingfast.com/java.
От издательства
Ваши замечания, предложения, вопросы отправляйте по адресу: comp@piter.com (издательство «Питер», компьютерная редакция).
Мы будем рады узнать ваше мнение!
На веб-сайте издательства https://www.piter.com вы найдете подробную информацию о наших книгах.
1. Знакомство с Java
Добро пожаловать в мир программирования Java… и спасибо за то, что вы выбрали мою книгу среди многих, написанных о Java.
Эта книга предназначена для того, чтобы помочь в быстром изучении программирования Java — как опытному программисту, так и полному профану. Материал был тщательно подобран для того, чтобы представить читателю широкий спектр фундаментальных концепций Java, не перегружая его лишней информацией. Хотя изложить все концепции Java в одной книге невозможно, я уверяю вас, что к концу книги вы сможете без особых проблем писать программы на Java. Более того, мы совместно напишем программу в составе проекта в конце книги. Ну что, готовы?
Для начала ответим на несколько вопросов.
1.1. Что такое Java?
Java — объектно-ориентированный язык программирования, который был разработан Джеймсом Гослингом из компании Sun Microsystems (которая позже была приобретена компанией Oracle Corporation). Первая версия была выпущена в 1995 году. В настоящее время Java является одним из самых популярных языков программирования. Язык может использоваться для разработки приложений, предназначенных для разных рабочих сред: настольных приложений, веб-приложений и даже приложений для мобильных устройств. Одна из главных особенностей Java — платформенная независимость. Это означает, что программа, написанная на Java, может выполняться в любой операционной системе (Window, Mac или Linux).
Код Java, как и код всех современных языков программирования, напоминает английский текст. Компьютер такой текст не понимает. А это означает, что код Java должен быть преобразован в машинный код специальным процессом, который называется компиляцией. Каждая компьютерная платформа использует собственный набор машинных команд. Следовательно, машинный код, откомпилированный для одной платформы, на другой платформе работать не будет. В большинстве языков программирования (таких, как C и C++) написанный код компилируется непосредственно в машинный код. В результате такой машинный код может выполняться только на той конкретной платформе, для которой он был откомпилирован.
В Java все происходит иначе.
Вместо того чтобы компилировать исходную программу непосредственно в машинный код, Java сначала компилирует ее в байт-код. Байт-код не зависит от платформы. Иначе говоря, не существует различий между байт-кодом для Windows, Mac или Linux.
Когда пользователь пожелает запустить программу Java, специальная программа на его компьютере (виртуальная машина Java, или сокращенно JVM) преобразует байт-код в машинный код для конкретной платформы, на которой работает пользователь.
Процесс двухшаговой компиляции хорош тем, что он позволяет коду Java выполняться на всех платформах — при условии, что на компьютере, на котором работает программа Java, установлена JVM. Ее можно загрузить бесплатно; существуют разные версии для разных компьютерных платформ. О том, как установить JVM, будет рассказано в следующей главе.
1.2. Зачем изучать Java?
Есть много причин, по которым стоит заняться изучением Java. Некоторые из них перечислены ниже.
Во-первых, в настоящее время Java является одним из самых распространенных языков программирования. По данным Oracle, Java работает на 3 миллиардах устройств. Более того, приложения Android также разрабатываются с использованием Java. С ростом спроса на мобильные приложения можно с уверенностью сказать, что для каждого, кто хочет стать программистом, изучение Java просто необходимо.
Во-вторых, по своему синтаксису и функциональным возможностям Java напоминает другие языки программирования (такие, как C и C++.) Если у вас уже есть опыт программирования, освоить Java будет проще простого. Даже если вы вообще ничего не знаете о программировании, будьте спокойны: Java проектировался как язык, который изучается относительно просто. Многие программисты считают, что изучить Java проще, чем, скажем, C или C++.
Язык Java также проектировался как платформенно-независимый. Как упоминалось ранее, код Java сначала компилируется в байт-код, который может выполняться на любой машине с JVM. А значит, Java позволяет написать код один раз, а потом выполнять его везде, где потребуется.
Кроме того, Java относится к семейству языков объектно-ориентированного программирования (ООП). ООП — подход к программированию, при котором задача разбивается на взаимодействующие друг с другом объекты. В этой книге будут рассмотрены различные концепции ООП. После того как вы освоите Java, вы начнете хорошо понимать эти концепции, а это упростит изучение других объектно-ориентированных языков в будущем.
Я убедил вас, что язык Java достоин вашего внимания? Поехали!
2. Подготовка к работе на Java
2.1. Установка JDK и NetBeans
Прежде чем браться за разработку приложений на Java, необходимо загрузить и установить JDK и NetBeans. Оба продукта доступны для бесплатной загрузки.
2.1.1. Что такое JDK?
JDK (Java Development Kit) — бесплатный набор инструментов от компании Oracle, упрощающих разработку приложений Java. К числу таких инструментов относится компилятор для преобразования написанного кода в байт-код (javac.exe), архиватор для упаковки и распространения файлов Java (jar.exe) и генератор документации для построения документации в формате HTML на основе кода Java (javadoc.exe).
Кроме того, JDK также включает исполнительную среду Java (Java Runtime Environment, или JRE). JRE содержит виртуальную машину Java (JVM), упомянутую в главе 1, и ресурсы, необходимые JVM для запуска программ Java.
Если вас интересует только возможность запуска программ Java, то ничего, кроме JRE, вам не понадобится. Но поскольку мы также собираемся разрабатывать программы Java, нам понадобится JDK.
Чтобы загрузить JDK, откройте страницу https://www.oracle.com/java/technologies/javase-jdk11-downloads.html и прокрутите ее до нижнего края. Найдите таблицу с несколькими ссылками для загрузки. Версия, которую вам нужно загрузить, зависит от используемой операционной системы. Обозначения x86 и x64 соответствуют 32- и 64-разрядным операционным системам соответственно. Например, если вы используете 64-разрядную операционную систему Windows, следует загрузить файл Windows-x64 Installer.
Когда файл будет загружен, сделайте двойной щелчок на загруженном файле, чтобы установить JDK.
2.1.2. Что такое NetBeans?
Кроме JDK вам также понадобится установить NetBeans.
NetBeans — интегрированная среда разработки (IDE), которой мы будем пользоваться для упрощения процесса программирования. Строго говоря, приложения Java можно разрабатывать и без NetBeans. Вы можете писать код в Блокноте (или любом другом текстовом редакторе), а потом откомпилировать и выполнить его с помощью инструментов, предоставляемых JDK. На следующей иллюстрации показано, как это делается.
Впрочем, хотя приложения Java можно разрабатывать с использованием одного JDK, процесс разработки получается утомительным и ненадежным.
Чтобы программирование стало более приятным, я настоятельно рекомендую воспользоваться интегрированной средой. В IDE входит текстовый редактор с расширенной функциональностью для написания кода, а также графическим интерфейсом для отладки, компиляции и запуска приложений. Как будет показано позднее, эти функции очень сильно упрощают программирование. В книге будет использоваться интегрированная среда NetBeans, предоставляемая компанией Oracle.
Чтобы загрузить NetBeans, откройте страницу https://netbeans.apache.org/download/nb90/nb90.html. Прокрутите ее до раздела Downloading и щелкните на ссылке Binaries, чтобы загрузить нужный файл. После того как файл будет загружен, распакуйте его на рабочем столе.
После того как это будет сделано, можно переходить к написанию вашей первой программы на Java.
2.2. Как пользоваться этой книгой?
Прежде чем браться за программирование, я хотел бы подчеркнуть тот факт, что большая часть кода Java состоит из довольно длинных команд. В книге некоторые команды могут переноситься на следующую строку. Если у вас возникнут проблемы с чтением примеров кода, исходный код всех примеров программ можно загрузить по адресу: https://www.learncodingfast.com/java.
2.3. Ваша первая программа на Java
А теперь напишем свою первую программу.
Для начала перейдите в папку netbeans\bin внутри папки с распакованной установкой NetBeans (на рабочем столе). Пользователи Windows для запуска NetBeans должны сделать двойной щелчок на файле netbeans.exe (для 32-разрядных компьютеров) или netbeans64.exe (для 64-разрядных компьютеров). Пользователи Mac запускают NetBeans двойным щелчком на файле netbeans.
Если вы получите сообщение об ошибке, в котором говорится «Не удалось найти Java 1.8 или выше», необходимо сообщить NetBeans, где установлен пакет JDK.
В системе Windows JDK, скорее всего, будет установлен в папке C:\ProgramFiles\Java\jdk-***.
У пользователей Mac, вероятно, он будет установлен в папке /Library/Java/JavaVirtualMachines/jdk-***.jdk/Contents/Home.
В обоих случаях *** обозначает установленную версию.
Чтобы сообщить NetBeans, в какой папке находится установленная версия JDK, перейдите в папку netbeans\etc в папке установки NetBeans. Откройте файл netbeans.conf в любом текстовом редакторе (например, в Блокноте) и с помощью функции поиска в своем текстовом редакторе найдите строку с текстом netbeans_jdkhome. Если строка закомментирована (т. е. начинается с символа #), удалите символ #. Затем в значении параметра netbeans_jdkhome укажите путь к установке JDK.
Например, если JDK находится в папке C:\Program Files\Java\jdk-11.0.1, строка с параметром netbeans_jdkhome должна иметь вид
netbeans_jdkhome="C:\Program Files\Java\jdk-11.0.1"
Когда это будет сделано, сохраните и закройте файл netbeans.conf.
Снова запустите NetBeans. На этот раз интегрированная среда должна запуститься без ошибок.
Если вам будет предложено установить библиотеку nb-javac, выполните требование и установите ее.
Затем выберите в верхней строке меню команду File—>New Project…
На экране появляется диалоговое окно New Project. Выберите в списке Categories вариант Java, а в списке Projects — вариант Java Application. Щелкните на кнопке Next, чтобы перейти к следующему шагу.
На следующем экране введите имя проекта HelloWorld; обратите внимание на то, в какой папке хранится проект. Наконец, щелкните на кнопке Finish, чтобы создать проект.
Вы получите стандартную заготовку, которую NetBeans создаст для вас автоматически. Замените код в шаблоне тем, который приведен ниже.
Учтите, что номера строк включены только для удобства; они не являются частью кода. Добавьте закладку на этой странице, чтобы вам было удобнее возвращаться к ней, когда мы будем разбирать программу. Исходный код этого и всех прочих примеров можно загрузить по адресу: https://www.learncodingfast.com/java.
Если вам не хочется вручную набирать всю программу, приведенную ниже, вы можете просто удалить строки с символами / и * в левой части шаблона и добавить в него строки 6 и 7:
1 package helloworld;
2
3 public class HelloWorld {
4
5 public static void main(String[] args) {
6 // Выводит текст Hello World на экран
7 System.out.println("Hello World");
8 }
9
10 }
Тем не менее я настоятельно рекомендую ввести код самостоятельно — это позволит вам лучше почувствовать, как работает NetBeans. В процессе ввода вы непременно заметите некоторые интересные особенности NetBeans. Например, слова в тексте выделяются разными цветами. Это делается для того, чтобы программа лучше читалась. Дело в том, что разные слова используются в программе для разных целей. Эта тема будет более подробно рассмотрена в следующих главах.
Кроме того, вы заметите, что рядом с курсором время от времени появляется подсказка. Эта функция называется Intellisense. Например, когда вы вводите точку (.) после слова System, слева появляется список элементов, которые могут быть введены после точки, а также поле с дополнительной информацией.
Наконец, обратите внимание на то, что NetBeans при вводе открывающей скобки автоматически вставляет парную закрывающую скобку. Например, если ввести (, то NetBeans добавит символ ) за вас.
Также NetBeans предоставляет ряд других возможностей, которые упрощают программирование.
После того как вы завершите ввод, сохраните программу командой FileSave. В NetBeans поддерживается режим «компиляции при сохранении», в котором код автоматически компилируется каждый раз, когда вы его сохраняете. Чтобы выполнить откомпилированную программу, щелкните на кнопке Run на панели инструментов (см. ниже).
Если программа не запустится, на экране появляется окно с сообщением об ошибке. Щелкните на кнопке Run Anyway. В окне вывода появляется описание ошибки, показанное на иллюстрации. Также вы можете навести указатель мыши на красную волнистую линию в окне текстового редактора. Вы получите другую подсказку о том, что могло пойти не так. Попробуйте найти и исправить ошибку, а потом снова запустите программу.
Если все прошло нормально, то в окне вывода появится следующее сообщение:
run:
Hello World
BUILD SUCCESSFUL (total time: 0 seconds)
Программа просто выводит текст «Hello World» в окне вывода. Две другие строки содержат дополнительную информацию, которую предоставляет NetBeans, и не являются частью вывода.
Вот и все! Вы только что успешно написали свою первую программу. Возьмите пирожок, заслужили.
Программа сохраняется в файле Java с именем HelloWorld.java. Имя файла отображается в верхней части окна текстового редактора (см. предыдущую иллюстрацию).
2.4. Базовая структура программы Java
А теперь кратко пройдемся по базовой программе, которую вы только что написали.
2.4.1. Пакет
В первой строке располагается команда
package helloworld;
Она сообщает компилятору, что файл Java, который вы создали, является частью пакета helloworld.
Пакет — это просто набор взаимосвязанных классов и интерфейсов. Не беспокойтесь, если вы пока не знаете, что такое классы и интерфейсы, — я объясню смысл этих терминов в следующих главах.
Вставляя строку packagehelloworld; в начало своего файла, вы тем самым приказываете компилятору включить этот файл в пакет helloworld. Компилятор создает папку с именем helloworld и сохраняет файл в этой папке. Файлы, принадлежащие одному пакету, хранятся в одной папке.
Если открыть папку NetBeansProjects, вы обнаружите в ней папку с именем HelloWorld. Папка NetBeansProjects обычно находится в папке Documents. Если вам не удается найти эту папку, попробуйте воспользоваться функциями поиска вашего компьютера.
В папке HelloWorld содержится папка src, которая содержит папку helloworld.
В этой папке хранятся файлы пакета helloworld. По общепринятым соглашениям, имена пакетов записываются символами нижнего регистра. Следует помнить, что в языке Java различается регистр символов. Следовательно, HelloWorld и helloworld считаются двумя разными строками. В папке helloworld находится файл HelloWorld.java.
Главное преимущество объявления пакетов заключается в том, что они предотвращают конфликты имен. Два и более файла могут иметь одинаковые имена при условии, что они принадлежат разным пакетам, — по аналогии с тем, как на вашем компьютере могут существовать два и более одноименных файла при условии, что они хранятся в разных папках. О том, как создавать другие пакеты, рассказано в главе 8.
Кроме пакетов, создаваемых программистами, в поставку Java также включается множество заранее созданных пакетов с кодом, который вы можете использовать в своих программах. Например, код операций ввода и вывода объединен в пакет java.io, тогда как код реализации компонентов графического интерфейса (кнопок, меню и т.д.) находится в пакете java.awt. Чтобы использовать готовые пакеты, необходимо импортировать их в вашу программу. Вскоре я покажу, как это делается.
2.4.2. Класс HelloWorld
А теперь перейдем к классу HelloWorld. Классы будут более подробно описаны в главе 7. А пока достаточно знать, что в нашем примере класс HelloWorld начинается в строке 3 с открывающей фигурной скобки и завершается в строке 10 закрывающей фигурной скобкой. Фигурные скобки {} широко используются в Java для обозначения начала и конца элементов кода. Все открывающие скобки в Java должны иметь парные закрывающие скобки.
Класс HelloWorld содержит метод main(). Метод начинается в строке 5 и завершается в строке 8.
2.4.3. Метод main()
Метод main() является точкой входа всех приложений Java. Каждый раз, когда вы запускаете приложение Java, метод main() становится первым вызываемым методом вашего приложения.
Видите слова String[]args, заключенные в круглые скобки в заголовке метода main()? Это означает, что метод main() получает на входе массив строк. Пока не обращайте на них внимания. Массивы и входные данные будут рассматриваться в последующих главах.
В нашем примере метод main() содержит всего две строки кода. Первая строка
//Выводит текст Hello World на экран
называется комментарием. Комментарии игнорируются компилятором.
Вторая строка
System.out.println("Hello World");
выводит текст «Hello World» (без кавычек) в окне вывода (в нижней части экрана). Обратите внимание на то, что эта команда завершается символом ; (точка с запятой). Все команды в Java должны завершаться символом ;. В этом отношении язык Java похож на большинство других языков программирования, включая C и C++.
После команды System.out.println("Hello World"); код примера завершается двумя закрывающими фигурными скобками, которые соответствуют двум предшествующим открывающим фигурным скобкам.
Вот и все! Мы рассмотрели первую программу от первой строки до последней.
2.5. Комментарии
В этой главе вы узнали много нового. Вы уже в общих чертах представляете, как программировать на Java, и немного освоились с NetBeans. Но прежде чем завершать эту главу, я должен рассказать еще об одной возможности языка Java — о комментариях. В предыдущем разделе я упоминал о том, что строка
// Выводит текст Hello World на экран
представляет собой комментарий и игнорируется компилятором.
Комментарий не является частью программы. Он добавляется в код только для того, чтобы сделать его более понятным для других программистов. Комментарии не преобразуются в байт-код.
Чтобы включить комментарии в программу, поставьте два символа // в начале каждой строки комментария:
// Комментарий
// Другой комментарий
// И еще один комментарий
Также можно воспользоваться конструкцией /* … */ для создания многострочных комментариев:
/* Комментарий
Другой комментарий
И еще один комментарий
*/
Комментарии также могут размещаться после команд:
System.out.println("Hello"); // Выводит слово Hello
3. Переменные и операторы
Теперь, когда вы познакомились с NetBeans и в общих чертах поняли, как устроена программа Java, можно переходить к более серьезным темам. В этой главе вы узнаете о переменных и операторах. А конкретнее, вы узнаете, что такое переменные, как назначать им имена, объявлять и инициализировать их. Также будут рассмотрены основные операции, которые можно выполнять с переменными.
3.1. Что такое переменные?
Переменные — это имена, которые назначаются данным для хранения и обработки их в программах. Представьте, что ваша программа должна хранить возраст пользователя. Для этого мы можем присвоить данным имя userAge и объявить переменную userAge следующей командой:
int userAge;
В этом объявлении сначала указывается тип данных переменной, за которым следует имя. Под типом данных переменной понимается тип данных, которые будут храниться в переменной (например, число или фрагмент текста). В нашем примере используется тип данных int, что соответствует целому числу.
Переменной назначается имя userAge.
После того как в программе будет объявлена переменная userAge, программа выделяет в пространстве памяти вашего компьютера блок, достаточный для хранения данных. После этого вы сможете обращаться к этим данным и изменять их, используя для этого имя userAge.
3.2. Примитивные типы данных в Java
В Java существуют восемь заранее определенных базовых типов данных. Они называются примитивными типами данных.
Первые четыре типа данных предназначены для хранения целых чисел (т. е. чисел, не имеющих дробной части):
• byte — тип данных byte используется для хранения целых чисел от –128 до 127. Он использует один байт памяти для хранения данных (размер типа данных, также называемый его шириной). Обычно тип данных byte используется в том случае, если пространство памяти очень ограничено или вы абсолютно уверены, что значение переменной ни при каких условиях не выйдет за пределы диапазона от –128 до 127.
Например, тип данных byte может использоваться для хранения возраста пользователя — крайне маловероятно, чтобы он когда-либо превысил 127.
• short — тип данных short использует 2 байта памяти и может использоваться для хранения значений в диапазоне от –32 768 до 32 767.
• int — тип данных int использует 4 байта памяти и может использоваться для хранения значений в диапазоне от –231 (–2 147 483 648) до 231–1 (2 147483 647). Этот тип данных чаще всего используется для хранения целых чисел, потому что его диапазон представления чаще всего используется на практике.
• long — тип данных long использует 8 байтов памяти и может использоваться для хранения значений в диапазоне от –263 до 263–1. Он используется реже, чем тип int, если только в вашей программе не возникает необходимость хранения очень больших чисел (например, населения Земли). Чтобы записать значение типа long в программе, добавьте суффикс L в конец числа. Суффиксы будут более подробно рассмотрены в следующем разделе.
Кроме типов данных для хранения целых чисел вам также понадобятся типы данных для хранения вещественных чисел (например, чисел с дробной частью).
float — тип данных float использует 4 байта памяти и может использоваться для хранения значений в диапазоне приблизительно от –3.40282347 × 1038 до 3.40282347 × 1038. Он обеспечивает точность представления приблизительно до 7 цифр. Это означает, что если вы используете тип float для хранения числа вида 1.23456789 (10 цифр), то число будет округлено приблизительно до 7 цифр (т. е.1.234568).
double — тип данных double использует 8 байт памяти и может использоваться для хранения значений в диапазоне приблизительно от –1.79769313486231570 × 10308 до 1.79769313486231570 × 10308 с точностью представления приблизительно до 15 цифр.
Когда вы определяете число с плавающей точкой в Java, по умолчанию оно автоматически считается относящимся к типу double, а не float. Если вы хотите, чтобы в Java число с плавающей точкой интерпретировалось как float, добавьте суффикс F в конец числа.
Если только в вашей системе нет серьезной нехватки памяти, всегда используйте double вместо float, так как этот тип обеспечивает бˆольшую точность.
Кроме шести типов данных, упоминавшихся выше, в Java существуют еще два примитивных типа данных:
• char — тип данных для представления отдельных символов Юникода: A, %, @, p и т.д. Использует 2 байта памяти;
• boolean — специальный тип данных, который может использоваться для хранения только двух значений: true и false. Чаще всего тип boolean используется в управляющих командах, которые будут рассматриваться в главе 6.
3.3. Выбор имени переменной
Имя переменной в Java может состоять только из букв, цифр, символов подчеркивания (_) или знаков доллара ($). При этом первым символом не может быть цифра. Таким образом, переменным могут быть присвоены имена _userName, $username, username или userName2, но не 2userName.
Впрочем, по общепринятым соглашениям, имена переменных должны начинаться с буквы, но не с $ или _. Кроме того, знак $ почти никогда не используется в именах переменных (хотя с технической точки зрения его использование ошибкой не является).
Имена переменных должны быть короткими, но содержательными; они должны передавать случайному читателю смысл имени. Переменным следует назначать имена вида userName, userAge и userNumber, но не n, a или un.
Кроме того, существуют некоторые зарезервированные слова, которые не могут использоваться как имена переменных, потому что в Java за ними уже изначально закреплен строго определенный смысл. К числу таких зарезервированных слов относятся System, while и т.д. Все эти слова будут описаны в следующих главах.
Среди программистов принято использовать схему верблюжьего регистра при назначении имен в Java. Под этим термином понимается практика записи слов в смешанном регистре, при котором каждое слово начинается с буквы верхнего регистра, кроме первого слова (например, thisIsAVariableName). Эта схема будет применяться в данной книге.
Наконец, в именах переменных различается регистр символов. Так, thisIsAVariableName и thisisavariablename считаются разными именами.
3.4. Инициализация переменной
Каждый раз, когда вы объявляете новую переменную, ей необходимо присвоить исходное значение. Присваивание исходного значения называется инициализацией переменной. Позднее вы сможете изменить значение переменной в своей программе.
Существуют два способа инициализации переменных: в точке объявления или в отдельной команде.
Следующий пример кода показывает, как инициализировать переменные в точке объявления (номера строк включены только для удобства; они не являются частью кода1).
1 byte userAge = 20;
2 short numberOfStudents = 45;
3 int numberOfEmployees = 500;
4 long numberOfInhabitants = 21021313012678L;
5
6 float hourlyRate = 60.5F;
7 double numberOfHours = 5120.5;
8
9 char grade = 'A';
10 boolean promote = true;
11
12 byte level = 2, userExperience = 5;
Как упоминалось ранее, для определения значения типа long следует добавить суффикс L в конец числа. Именно это было сделано в строке 4 при инициализации numberOfInhabitants. Если это не было сделано, компилятор сообщит о том, что число слишком велико, и выдаст сообщение об ошибке.
Кроме того, при инициализации переменной hourlyRate в строке 6 добавляется суффикс F. Дело в том, что по умолчанию любое число с плавающей точкой в Java интерпретируется как double. Суффикс F сообщает компилятору, что hourlyRate относится к типу данных float.
Наконец, при инициализации типа данных char необходимо заключить символ в апострофы, как в строке 9.
В строке 12 приведен пример объявления и инициализации двух переменных одного типа данных в одной команде. Имена переменных разделяются запятой, и обозначать тип данных второй переменной не обязательно.
Предыдущие примеры показывают, как инициализировать переменную в точке объявления. Также можно выбрать объявление и инициализацию переменной в двух разных командах, как показано ниже:
byte year; // Сначала переменная объявляется
year = 20; // А потом инициализируется
3.5. Оператор присваивания
Знак равенства = в программировании отличается по смыслу от того, что мы изучали в курсе математики. В программировании знак = называется оператором присваивания. Он означает, что значение справа от знака = присваивается переменной, указанной слева от знака.
В программировании команды x = y и y = x имеют совершенно разный смысл.
Удивлены? Следующий пример все прояснит.
Предположим, две переменные x и y объявляются следующим образом:
int x = 5;
int y = 10;
Если в программе встретится команда
x = y;
ваш учитель математики запротестует, потому что переменная x не равна y. Однако в программировании такая запись вполне нормальна.
Данная команда означает, что значение y присваивается переменной x. Присваивание значения переменной другой переменной — вполне нормальная операция. В нашем примере значение x становится равным 10, тогда как значение y остается неизменным. Иначе говоря, теперь x = 10 и y = 10.
Если теперь изменить значения x и y на 3 и 20 соответственно:
x = 3;
y = 20;
а потом выполнить команду
y = x;
значение x будет присвоено переменной y. Следовательно, переменная y примет значение 3, тогда как переменная x сохранит прежнее значение (т. е. после этого y = 3, x = 3).
3.6. Базовые операторы
Помимо присваивания исходного значения переменной или присваивания другой переменной с переменными также можно выполнять обычные математические операции. К числу базовых математических операторов Java относятся +, -, *, / и %, выполняющие операции сложения, вычитания, умножения, деления и вычисления остатка от целочисленного деления соответственно.
Пример
Допустим, x = 7, y = 2.
Сложение: x + y = 9.
Вычитание: x – y = 5.
Умножение: x * y = 14.
Деление: x/y = 3 (результат округляется в меньшую сторону до ближайшего целого).
Остаток: x % y = 1 (остаток от деления 7 на 2).
В Java операция деления дает целый результат, если оба операнда x и y являются целыми числами. Но если x и/или y не является целым числом, то и ответ не будет целочисленным. Пример:
7/2 = 3.
7.0/2 = 3,5.
7/2.0 = 3,5.
7.0/2.0 = 3,5.
В первом случае при делении целого числа на другое целое число вы получаете целочисленный ответ. Дробная часть ответа, если она и была, пропадает. Из-за этого мы получаем 3 вместо 3,5.
Во всех остальных случаях результат не является целым числом, если хотя бы один операнд не целое число.
Обратите внимание: 7.0 в Java — не то же самое, что 7. Первое является числом с плавающей точкой, тогда как второе — целое число.
3.7. Другие операторы присваивания
Кроме оператора = в Java (и в большинстве других языков программирования) существуют и другие операторы присваивания. К их числу относятся такие операторы, как +=, -= и *=.
Допустим, имеется переменная x с исходным значением 10. Если вы хотите увеличить x на 2, можно использовать запись вида
x = x + 2;
Программа сначала вычисляет значение в правой части (x + 2), а затем присваивает ответ переменной в левой части. Таким образом переменная x становится равной 12.
Вместо записи x = x + 2 можно использовать команду x += 2 для выполнения той же операции. Оператор += представляет собой сокращенную запись, которая объединяет оператор присваивания с оператором сложения. Таким образом, x += 2 означает просто x = x + 2.
Подобным образом для выполнения умножения можно использовать запись x = x - 2 или x -= 2. Это относится ко всем 5 операторам, упомянутым в предыдущем разделе.
Во многих языках программирования также поддерживаются операторы ++ и ––. Оператор ++ увеличивает значение переменной на 1. Допустим, имеется переменная
int x = 2;
После выполнения команды
x++;
переменная x будет содержать значение 3.
При использовании оператора ++ в операторе = уже нет необходимости. Команда x++; эквивалентна команде
x = x + 1;
Оператор ++ может размещаться перед именем переменной или после него. Это влияет на порядок выполнения операций.
Допустим, имеется целочисленная переменная с именем counter. При выполнении команды
System.out.println(counter++);
программа сначала выведет исходное значение counter, а потом увеличит counter на 1. Другими словами, операции выполняются в следующем порядке:
System.out.println(counter);
counter = counter + 1;
С другой стороны, при выполнении команды
System.out.println(++counter);
программа увеличит counter на 1 до того, как вывести новое значение counter. Иначе говоря, операции выполняются в порядке
counter = counter + 1;
System.out.println(counter);
Кроме оператора ++ также существует оператор –– (два минуса). Этот оператор уменьшает значение переменной на 1.
3.8. Преобразование типов в Java
Иногда в программе требуется преобразовать значение одного типа данных к другому типу — скажем, превратить double в int. Этот механизм называется преобразованием типов.
Если вы хотите преобразовать меньший тип данных к большему, ничего явно делать не придется. Например, приведенный ниже фрагмент кода присваивает значение short (2 байта) переменной double (8 байт). Такое преобразование называется расширяющимпримитивным преобразованием и не требует никаких специальных мер с вашей стороны:
short age = 10;
double myDouble = age;
Но если вы захотите присвоить больший тип данных переменной меньшего типа, необходимо явно обозначить преобразование парой круглых скобок. Такое преобразование называется сужающим примитивным преобразованием. Следующий пример показывает, как это делается.
int x = (int) 20.9;
Здесь значение типа double (8 байтов) преобразуется в int (4 байта).
Сужающее преобразование небезопасно, и его следует избегать, если только это возможно. Дело в том, что сужающее преобразование может привести к потере данных. При преобразовании значения 20.9 в int будет получен результат 20, а не 21. Дробная часть пропадает после преобразования.
Также значение double можно преобразовать к типу float. Вспомните, о чем говорилось ранее: в языке Java все дробные значения по умолчанию интерпретируются как double. Если вы хотите присвоить число 20.9 переменной float, к числу нужно будет добавить суффикс F. Также можно воспользоваться преобразованием типа
float num1 = (float) 20.9;
Переменной num1 будет присвоено значение 20.9.
Кроме преобразования между числовыми типами также можно выполнять другие виды преобразований. Некоторые из них будут рассмотрены в следующих главах.
1 В Java дробная часть отделяется точкой. — Примеч. ред.
В Java дробная часть отделяется точкой. — Примеч. ред.
Следующий пример кода показывает, как инициализировать переменные в точке объявления (номера строк включены только для удобства; они не являются частью кода1).
4. Массивы и строки
В предыдущей главе были рассмотрены восемь примитивных типов данных Java. Кроме примитивных типов Java также поддерживает более сложные типы данных. В этой главе будут рассмотрены два таких типа: строки и массивы. Кроме того, мы обсудим, чем примитивные типы данных отличаются от ссылочных типов.
4.1. Тип String
Начнем со строк, представленных типом String. Строка представляет собой фрагмент текста — например, «Hello World» или «Good morning».
Чтобы объявить и инициализировать переменную типа String, включите в программу строку следующего вида:
String message = "Hello World";
где message — имя переменной типа String, а "Hello World" — присваиваемая ей строка. Обратите внимание: текст строки заключается в двойные кавычки (").
Переменной типа String также можно присвоить пустую строку:
String anotherMessage = "";
Для соединения двух и более строк используется операция конкатенации (+). Например, команда
String myName = "Hello World, " + "my name is Jamie";
эквивалентна команде
String myName = "Hello World, my name is Jamie";
4.1.1. Методы String
В отличие от 8 примитивных типов, представленных в предыдущей главе, тип String в действительности является объектным. Иначе говоря, значение String является объектом класса String.
Не беспокойтесь, если вы не понимаете, что это значит; классы и объекты будут рассматриваться в главе 7. А пока достаточно знать, что класс String предоставляет набор готовых методов, которые могут использоваться при работе со строками. Метод представляет собой блок кода, рассчитанного на повторное использование, который решает определенную задачу. Некоторые примеры будут рассмотрены позднее.
В Java метод может существовать в нескольких вариантах. В большинстве примеров, рассмотренных ниже, рассматривается только одна разновидность каждого метода. Впрочем, если вы научитесь использовать одну разновидность, разобраться с другими разновидностями будет относительно не сложно. Рассмотрим некоторые часто используемые методы String.
length()
Метод length() сообщает общее количество символов, содержащихся в строке.
Например, для получения длины строки «Hello World» можно воспользоваться следующей командой:
"Hello World".length();
При использовании метода необходимо использовать оператор «точка» (.). За точкой следует имя метода (length в данном случае) и пара круглых скобок (). Многие методы возвращают ответ после выполнения своих задач. Метод length() возвращает длину строки. Результат можно присвоить переменной:
int myLength = "Hello World".length();
В этом примере значение myLength будет равно 11, так как каждая из строк «Hello» и «World» состоит из 5 символов. Если же добавить пробел, разделяющий два слова, мы получим длину 11.
Результат метода length() можно вывести следующими командами:
int myLength = "Hello World".length();
System.out.println(myLength);
Попробуйте добавить эти две команды в файл HelloWorld.java, созданный в главе 2. Команды должны быть добавлены между открывающей и закрывающей фигурной скобкой метода main(). Запустите программу. Вы увидите, что в выходных данных программы выводится значение 11. Вывод результатов будет более подробно рассмотрен в следующей главе.
toUpperCase()/toLowerCase()
Метод toUpperCase() используется для преобразования строки к верхнему регистру. Метод toLowerCase() используется для преобразования строки к нижнему регистру.
Например, чтобы преобразовать строку «Hello World» к верхнему регистру, можно использовать следующую команду:
String uCase = "Hello World".toUpperCase();
В правой части команды строка «Hello World» используется для вызова метода toUpperCase(). Результат присваивается переменной uCase.
Переменная uCase будет содержать строку «HELLO WORLD».
substring()
Метод substring() предназначен для выделения подстроки из более длинной строки.
Некоторым методам Java для работы необходимы исходные данные. Такие данные называются аргументами. Эти аргументы заключаются в круглые скобки, следующие за именем метода. Например, методу substring() для работы необходим один аргумент.
Например, для выделения подстроки из строки «Hello World» используется следующая команда:
String firstSubstring = "Hello World".substring(6);
В правой части команды строка «Hello World» используется для вызова метода substring(). Число 6 в круглых скобках называется аргументом. Этот аргумент сообщает компилятору, с какой позиции следует начинать извлечение подстроки. По сути вы даете команду компилятору выделить подстроку от индекса 6 (т. е. позиции 6) и до конца строки.
Следует учитывать, что в программировании нумерация индексов начинается с НУЛЯ, а не с 1. Это стандартная практика почти во всех языках программирования, включая Python и Java. Следовательно, в нашем примере символ ‘H’ имеет индекс 0, а символ ‘W’ — индекс 6.
Приведенная выше команда извлекает подстроку «World», которая затем присваивается переменной firstSubstring.
Таким образом, firstSubstring будет содержать текст «World».
У метода substring() также существует другая разновидность, которая позволяет выделить подстроку от одного индекса до другого. Если вам потребуется выделить подстроку от позиции 1 до позиции 7, это можно сделать так:
String message = "Hello World";
String secondSubstring = message.substring(1, 8);
В этом примере строка «Hello World» сначала присваивается переменной message. Затем строка message используется для вызова метода substring().
При вызове передаются два аргумента: 1 и 8.
Как и прежде, первый аргумент сообщает компилятору индекс начальной позиции для выделения строки. Второй аргумент сообщает компилятору индекс позиции, в которой выделение строки должно завершиться. Иначе говоря, в нашем примере компилятор остановит выделение подстроки в позиции 8. Это означает, что символ в позиции 8 не будет включен в подстроку. А значит, вызов метода выделит подстроку «ello Wo».
Итак, переменная secondSubstring содержит строку «ello Wo», а переменная message остается равной «Hello World».
charAt()
Метод charAt() возвращает один символ, находящийся в заданной позиции. Полученный символ может быть присвоен переменной типа char.
Например, команда
char myChar = "Hello World".charAt(1);
извлекает символ с индексом 1 и присваивает его myChar. После этого значение переменной myChar будет равно 'e'.
equals() — метод equals() используется для проверки равенства двух строк. Он возвращает true, если строки равны, и false в противном случае.
После выполнения следующих команд:
boolean equalsOrNot = "This is Jamie".equals("This is
Jamie");
boolean equalsOrNot2 = "This is Jamie".equals("Hello
World");
переменная equalsOrNot будет равна true, тогда как переменная equalsOrNot2 будет равна false.
split() — метод разбивает строку на подстроки по разделителям, определяемым пользователем. После разбиения строки метод split() возвращает массив полученных подстрок. Массив представляет собой коллекцию взаимосвязанных элементов. Массивы будут рассмотрены в следующем разделе.
Допустим, строку «Peter, John, Andy, David» требуется разбить на подстроки. Это можно сделать так:
String names = "Peter, John, Andy, David";
String[] splitNames = names.split(", ");
Сначала значение строки, которую требуется разбить, присваивается переменной names. Затем переменная names используется для вызова метода split(). Метод split() получает один аргумент — разделитель, который будет использоваться для разбиения строки. В нашем примере разделителем является запятая, за которой следует пробел.
В результате выполнения этого кода будет получен следующий массив:
{"Peter", "John", "Andy", "David"}
Массив присваивается переменной splitNames.
В этом разделе были рассмотрены некоторые часто используемые методы String в языке Java. За полным списком всех методов String обращайтесь на страницу https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#method.summary.
4.2. Массивы
Перейдем от строк к массивам.
Массив представляет собой набор данных, которые обычно как-то связаны друг с другом. Допустим, вы хотите сохранить в программе данные о возрасте 5 пользователей. Вместо того чтобы сохранить их под именами user1Age, user2Age, user3Age, user4Age и user5Age, их можно сохранить в массиве.
Переменную-массив можно объявить двумя способами. В первом варианте она объявляется следующим образом:
int[] userAge;
Тип int указывает, что в переменной будут храниться значения int.
[] указывает, что переменная является не обычной переменной, а массивом.
userAge — имя массива.
Во втором варианте она объявляется так:
int userAge[];
Этот стиль, происходящий из языка C/C++, был принят в Java для удобства программистов C/C++. Тем не менее этот вариант синтаксиса не считается предпочтительным в Java. В книге мы будем придерживаться первого стиля.
После объявления переменной-массива необходимо создать массив и присвоить его переменной. Для этого используется ключевое слово new:
int[] userAge;
userAge = new int[] {21, 22, 23, 24, 25};
Первая команда объявляет переменную-массив userAge. Вторая команда создает массив {21, 22, 23, 24, 25} и присваивает его переменной userAge. Так как переменной userAge еще не был присвоен никакой массив, команда инициализирует userAge созданным массивом. После инициализации массива изменить его размер уже не удастся. В данном случае массив userAge может использоваться для хранения только 5 значений, потому что он был инициализирован 5 целыми числами. {21, 22, 23, 24, 25} — пять целых чисел, которые хранятся в массиве в настоящий момент.
Кроме объявления и инициализации массива в двух командах можно объединить две команды с использованием сокращенного синтаксиса:
int[] userAge2 = new int[] {21, 22, 23, 24, 25};
Эту команду можно дополнительно упростить:
int[] userAge2 = {21, 22, 23, 24, 25};
Другими словами, слова new int[] можно опустить при объявлении и инициализации массива в одной команде.
Третий способ объявления и инициализации массива выглядит так:
int[] userAge3 = new int[5];
Команда объявляет массив userAge3 и инициализирует его массивом из 5 целых чисел (как указывает число 5 в квадратных скобках [ ]). Так как значения этих 5 чисел не указаны, Java автоматически создает массив, инициализирует элементы значением по умолчанию и присваивает его userAge3. Для целых чисел значение по умолчанию равно 0. Следовательно, переменная userAge3 будет содержать значение {0, 0, 0, 0, 0}.
Вы можете изменять значения отдельных элементов массива, обращаясь к ним по индексу. Еще раз напомню, что индексы всегда начинаются с 0. Первый элемент массива имеет индекс 0, следующий элемент имеет индекс 1 и т.д. Допустим, массив userAge в настоящее время содержит элементы {21, 22, 23, 24, 25}. Чтобы обновить первый элемент массива, выполните команду
userAge[0] = 31;
Массив принимает вид {31, 22, 23, 24, 25}.
Если же ввести команду
userAge[2] = userAge[2] + 20;
массив будет содержать элементы {31, 22, 43, 24, 25}. Иначе говоря, третий элемент увеличивается на 20.
4.2.1. Методы массивов
Массивы, как и строки, содержат набор готовых методов.
Методы, которые рассматриваются ниже, находятся в классе java.util.Arrays. Чтобы пользоваться ими, добавьте в свою программу команду
import java.util.Arrays;
Команда сообщает компилятору, где находится код этих методов.
Команда import должна располагаться в программе после команды package, но до объявления класса. Пример:
package helloworld;
import java.util.Arrays;
public class HelloWorld {
// Код класса HelloWorld
}
Но ведь ранее, когда мы использовали класс String, никакие команды import при этом не включались! Дело в том, что класс String входит в пакет java.lang, который импортируется по умолчанию во всех программах Java.
А теперь рассмотрим некоторые часто используемые методы массивов.
equals() — метод проверяет равенство двух массивов. Если массивы равны, то метод возвращает true, а если нет — false. Два массива считаются равными, если они содержат одинаковое количество элементов, а эти элементы равны и следуют в одинаковом порядке.
Допустим, имеется следующий фрагмент программы:
int[] arr1 = {0,2,4,6,8,10};
int[] arr2 = {0,2,4,6,8,10};
int[] arr3 = {10,8,6,4,2,0};
boolean result1 = Arrays.equals(arr1, arr2);
boolean result2 = Arrays.equals(arr1, arr3);
Переменная result1 будет равна true, а переменная result2 будет равна false. Такое значение result2 объясняется тем, что хотя arr1 и arr3 содержат одинаковые элементы, эти элементы следуют в обратном порядке. Соответственно эти два массива не считаются равными.
Обратите внимание: в этом примере перед именем метода добавлено слово Arrays. Дело в том, что все методы класса Arrays являются статическими. Чтобы вызвать статический метод, следует указать перед ним имя класса. Статические методы более подробно рассматриваются в главе 7.
copyOfRange() — метод копирует содержимое одного массива в другой массив. При вызове он получает три аргумента.
Допустим, имеется следующий массив:
int [] source = {12, 1, 5, -2, 16, 14, 18, 20, 25};
Содержимое source можно скопировать в новый массив dest следующей командой:
int[] dest = Arrays.copyOfRange(source, 3, 7);
Первый аргумент (source) определяет массив с копируемыми значениями. Второй и третий аргументы сообщают компилятору, на каком индексе должно начинаться и останавливаться копирование соответственно. Иначе говоря, в нашем примере копируются элементы от индекса 3 до индекса 6 включительно (т. е. элемент с индексом 7 не копируется).
После копирования элементов метод copyOfRange() возвращает массив со скопированными числами. Этот массив присваивается dest.
Таким образом, массив dest после копирования содержит элементы {-2, 16, 14, 18}, тогда как массив source остается неизменным.
toString() — метод возвращает объект String, представляющий элементы массива. Такое преобразование упрощает вывод содержимого массива. Допустим, имеется массив
int[] numbers = {1, 2, 3, 4, 5};
Следующая команда может использоваться для вывода содержимого numbers.
System.out.println(Arrays.toString(numbers));
Команда выводит строку
[1, 2, 3, 4, 5]
в окне вывода.
sort() — метод предназначен для сортировки массивов. В аргументе ему передается массив.
Допустим, имеется массив
int [] numbers2 = {12, 1, 5, -2, 16, 14};
Чтобы отсортировать массив, выполните следующую команду:
Arrays.sort(numbers2);
Массив будет отсортирован по возрастанию.
Метод sort() не возвращает новый массив. Он просто изменяет массив, переданный при вызове. Другими словами, он изменяет массив numbers2 в нашем примере. После этого можно воспользоваться командой
System.out.println(Arrays.toString(numbers2));
для вывода отсортированного массива. Команда выдает результат
[-2, 1, 5, 12, 14, 16]
binarySearch() — метод ищет конкретное значение в отсортированном массиве. Чтобы использовать этот метод, необходимо предварительно отсортировать массив. Для сортировки можно воспользоваться методом sort(), описанным выше.
Допустим, имеется следующий массив:
int[] myInt = {21, 23, 34, 45, 56, 78, 99};
Чтобы определить, присутствует ли значение 78 в массиве, выполните команду
int foundIndex = Arrays.binarySearch(myInt, 78);
Значение foundIndex будет равно 5. Оно показывает, что число 78 находится в элементе массива с индексом 5.
С другой стороны, при выполнении команды
int foundIndex2 = Arrays.binarySearch(myInt, 39);
значение foundIndex2 будет равно -4.
Этот результат состоит из двух частей — знака «-» и числа 4.
Знак «-» просто означает, что значение 39 не найдено.
С числом 4 дело обстоит сложнее. Оно показывает, где бы находилось данное число, если бы оно существовало в массиве. При этом индекс увеличивается на 1. Другими словами, если бы число 39 присутствовало в массиве, то оно бы находилось в элементе с индексом 4-1=3.
В этом разделе были рассмотрены некоторые часто используемые методы массивов. За полным списком всех методов массивов обращайтесь на страницуhttps://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html.
4.2.2. Определение длины массива
Наконец, посмотрим, как можно определить длину массива. Длина массива сообщает количество элементов в массиве. Ранее при обсуждении строк мы упоминали, что для определения длины строк можно использовать метод length().
Как ни странно, для массивов метод length() не поддерживается. Для определения длины массива используется полеlength. О полях и методах будет рассказано в главе 7. А пока достаточно знать, что для получения длины массива не нужно добавлять круглые скобки после слова length.
Например, для массива
int [] userAge = {21, 22, 26, 32, 40};
значение userAge.length равно 5, так как массив содержит 5 чисел.
4.3. Примитивные типы и ссылочные типы
После знакомства с массивами и строками можно перейти к одной важной концепции, касающейся типов данных в Java.
Все типы данных в Java делятся на примитивные и ссылочные. В Java существует всего 8 примитивных типов (byte, short, int, long, float, double, char и boolean), остальные типы являются ссылочными. К ссылочным типам принадлежат строки и массивы, упоминавшиеся в этой главе, а также классы и интерфейсы, которые будут рассматриваться в главах 7 и 8.
Одно из главных различий между примитивными и ссылочными типами связано с тем, какие данные хранятся в памяти типа.
Примитивный тип хранит свои собственные данные.
Когда мы пишем
int myNumber = 5;
в переменной myNumber хранится фактическое значение 5.
С другой стороны, ссылочный тип не хранит свои данные. Вместо этого хранится ссылка на данные. Ссылка не сообщает компилятору значение данных; она сообщает компилятору, где он сможет найти эти данные.
Примером ссылочного типа является String. Когда вы выполняете команду вида
String message = "Hello";
в переменной message не сохраняется строка «Hello».
Вместо этого строка «Hello» создается и сохраняется в другом месте памяти компьютера. В переменной message сохраняется адрес этого блока памяти.
Вот и все, что необходимо знать о ссылочных типах на данный момент. А поскольку книга предназначена для новичков, мы не будем подробно обсуждать, почему необходимы ссылочные типы. Просто помните о различиях между примитивным и ссылочным типом; в первом хранится значение, а во втором — адрес.
4.4. Строки и неизменяемость
Прежде чем завершить эту главу, я хотел бы упомянуть еще одну концепцию, относящуюся к строкам. А именно, что в Java (и многих других языках) строки неизменяемы.
Неизменяемость означает, что значение строки не может быть изменено. Каждый раз, когда вы обновляете переменную String, на самом деле вы создаете новую строку и присваиваете ее адрес в памяти переменной String. Рассмотрим пример. Допустим, имеется команда
String message = "Hello";
Ранее я упоминал о том, что компилятор создаст строку «Hello» и сохраняет ее где-то в памяти компьютера. В переменной message сохраняется адрес этого блока памяти. Если теперь обновить значение переменной message и присвоить ей строку «World»:
message = "World";
компилятор не обращается к блоку памяти, где хранится строка «Hello», чтобы изменить значение на «World». Вместо этого он создает новую строку «World» и сохраняет ее где-то в другом месте памяти компьютера. Новый адрес присваивается message. Иначе говоря, теперь в памяти находятся две строки: «Hello» и «World». В message хранится адрес «World». Если строка «Hello» в программе более не используется, она будет со временем уничтожена, чтобы освободить эту область памяти. Этот процесс, называемый уборкой мусора, автоматически предоставляется Java.
5. Интерактивность
Итак, мы рассмотрели основы работы с переменными и типами данных. Напишем программу, которая получает ввод от пользователей, сохраняет данные в переменной и выводит сообщение для пользователей. В конце концов, какой прок от компьютерной программы, если она не может взаимодействовать с пользователем?
5.1. Операторы вывода
Примеры вывода сообщений уже встречались нам в главах 2 и 4.
Проще говоря, для вывода результатов для пользователя можно воспользоваться методом print() или println(), предоставляемым Java. Чтобы пользоваться этими методами, необходимо поставить System.out перед именем метода. Это необходимо из-за того, что два метода принадлежат классу PrintStream и для обращения к ним необходимо наличие префикса System.out. Не беспокойтесь, если что-то сейчас покажется непонятным. Классы и методы более подробно рассматриваются в главе 7.
Методы println() и print() отличаются тем, что println() после вывода сообщения перемещает курсор к следующей строке, а метод print() этого не делает.
Например, при выполнении команды
System.out.println("Hello ");
System.out.println ("How are you?");
будет получен следующий результат:
Hello
How are you?
Если же использовать запись
System.out.print("Hello ");
System.out.print("How are you?");
результат будет таким:
Hello How are you?
В остальном эти два метода эквивалентны.
Рассмотрим несколько примеров использования println() для вывода сообщений. Метод print() работает точно так же.
5.1.1. Вывод простого текстового сообщения
Чтобы вывести простое сообщение, используйте вызов println():
System.out.println("Hi, my name is Jamie.");
Результат:
Hi, my name is Jamie.
5.1.2. Вывод значения переменной
Чтобы вывести значение переменной, передайте имя переменной как аргумент. Допустим, имеется следующая переменная:
int number = 30;
Значение number можно вывести следующей командой:
System.out.println(number);
Результат:
30
Обратите внимание: имя переменной (number) не заключается в двойные кавычки. Если выполнить команду
System.out.println("number");
то вместо значения будет выведена строка
number
5.1.3. Вывод результатов без присваивания переменной
Метод println() также может использоваться для прямого вывода результата математического выражения или метода.
Например, при выполнении следующей команды
System.out.println(30+5);
вы получите результат
35
Для вывода результата метода можно использовать вызов
System.out.println("Oracle".substring(1, 4));
Эта команда выводит результат вызова метода substring(). На выходе будет получена строка
rac
5.1.4. Использование конкатенации
А теперь рассмотрим еще несколько примеров вывода более сложных строк, полученных объединением двух и более коротких строк. Для выполнения этой операции (конкатенации) используется знак +.
Например, при выполнении команды
System.out.println("Hello, " + "how are you?" +
" I love Java.");
будет получен результат
Hello, how are you? I love Java.
Для конкатенации строк со значениями переменных можно воспользоваться конструкцией вида
int results = 79;
System.out.println("You scored " + results + " marks
for your test.");
Здесь строки «You scored » и «marks for your test.» объединяются с переменной results. Команда выводит следующий результат:
You scored 79 marks for your test.
Наконец, строки можно объединять с результатами математических выражений:
System.out.println("The sum of 50 and 2 is " +
(50 + 2) + ".");
Результат:
The sum of 50 and 2 is 52.
Обратите внимание: в этом примере математическое выражение 50+2 было заключено в круглые скобки. Они нужны для того, чтобы компилятор вычислил выражение до того, как объединять результат с двумя другими подстроками. Я настоятельно рекомендую делать это каждый раз, когда конкатенация объединяет строки с результатами математических выражений. Если вы этого не сделаете, это может привести к ошибке.
5.2. Служебные последовательности
А теперь рассмотрим служебные последовательности. Иногда в программах требуется вывести специальные «непечатаемые» символы — такие как табуляция или символ новой строки. В данном случае необходимо использовать символ \ (обратная косая черта) для экранирования символов, которые в обычной ситуации имеют другой смысл.
Например, для вывода табуляции перед символом t ставится обратная косая черта: \t. Без символа \ будет выведена буква «t». С символом \ выводится символ табуляции. \t называется служебной последовательностью. Если выполнить команду
System.out.println("Hello\tWorld");
результат будет выглядеть так:
Hello World
Другие часто используемые служебные последовательности:
5.2.1. Переход на новую строку (\n)
Пример:
System.out.println("Hello\nWorld");
Результат:
Hello
World
5.2.2. Вывод самого символа \ (\\)
Пример:
System.out.println("\\");
Результат:
\
5.2.3. Вывод двойной кавычки (\"), чтобы символ не интерпретировался как завершение строки
Пример:
System.out.println("I am 5'9\" tall");
Результат:
I am 5'9" tall
5.3. Форматирование вывода
В предыдущих примерах было показано, как выводить данные методами println() и print(). Тем не менее иногда требуется более точно управлять форматом выходных данных. Например, при выполнении команды
System.out.println("The answer for 5.45 divided by 3
is " + (5.45/3));
будет получен следующий результат:
The answer for 5.45 divided by 3 is 1.8166666666666667
В большинстве случаев пользователю просто не нужно столько знаков в дробной части. В таких ситуациях для вывода результата можно воспользоваться методом printf(). Метод printf() чуть сложнее метода println(), но он предоставляет больше возможностей для управления выводом. Чтобы отформатировать приведенный выше результат, можно воспользоваться командой
System.out.printf("The answer for %.3f divided by %d
is %.2f.", 5.45, 3, 5.45/3);
Результат будет выглядеть так:
The answer for 5.450 divided by 3 is 1.82.
Метод printf() получает один или несколько аргументов. В приведенном примере методу передаются четыре аргумента.
Первый аргумент "The answer for %.3f divided by %d is %.2f." содержит форматируемую строку.
Вероятно, вы заметили несколько странных символов в строке: %.3f, %d и %.2f. Они называются спецификаторами формата: резервируют место в выводимой строке и заменяются следующими аргументами. Первый спецификатор формата (%.3f) заменяется первым следующим аргументом (5.45), второй (%d) — вторым аргументом (3) и т.д.
Спецификаторы формата всегда начинаются со знака процента (%) и завершаются преобразователем (например, f или d). Они указывают, как должны форматироваться заменяющие их аргументы. Между знаком процента (%) и преобразователем может размещаться дополнительная информация — так называемые флаги.
В нашем примере первый спецификатор формата имеет вид %.3f.
f — преобразователь. Он сообщает компилятору, что аргумент должен быть заменен числом с плавающей точкой (т. е. числом с дробной частью — например, float или double). При попытке заменить его каким-то другим числом будет выдано сообщение об ошибке.
.3 — флаг. Он показывает, что число должно выводиться с 3 цифрами в дробной части. Таким образом, число 5,45 будет отображаться в виде 5.450.
Кроме спецификатора %.3f существует много других спецификаторов, которые могут использоваться в Java. В следующих двух разделах рассматриваются другие часто используемые преобразователи и флаги в спецификаторах.
5.3.1. Преобразователи
Целочисленный преобразователь d
Предназначен для форматирования целых чисел (например, byte, short, int и long).
Пример:
System.out.printf("%d", 12);
Результат:
12
Примечание: при попытке выполнения команды
System.out.printf("%d", 12.9);
произойдет ошибка, так как 12.9 не является целым числом.
Аналогичным образом System.out.printf("%f", 12); выдаст ошибку, так как 12 не является числом с плавающей точкой.
Преобразователь новой строки n
Переводит курсор на следующую строку.
Пример:
System.out.printf("%d%n%d", 12, 3);
Результат:
12
3
5.3.2. Флаги
Флаг ширины
Этот флаг используется для определения ширины вывода.
Пример 1:
System.out.printf("%8d", 12);
Результат:
12
В этом примере перед числом 12 выводится 6 пробелов, чтобы общая ширина вывода была равна 8.
Пример 2:
System.out.printf("%8.2f", 12.4);
Результат:
12.40
В этом примере перед числом выводятся 3 пробела, чтобы общая ширина вывода (с точкой — разделителем дробной части) была равна 8.
Флаг разделителя групп разрядов (,)
Флаг используется для вывода чисел с разделителем групп разрядов.
Пример 1:
System.out.printf("%,d", 12345);
Результат:
12,345
Пример 2:
System.out.printf("%,.2f", 12345.56789);
Результат:
12,345.57
5.4. Получение ввода от пользователя
Теперь, когда вы умеете выводить информацию для пользователя, нужно узнать, как получать от него данные. На самом деле получение ввода происходит довольно тривиально. Это можно делать несколькими способами, но самый простой и распространенный основан на использовании объекта Scanner.
Чтобы получить данные от пользователя, необходимо сначала импортировать класс Scanner следующей командой:
import java.util.Scanner;
Затем необходимо создать объект Scanner и передать System.in в аргументе.
System.in сообщает компилятору, что вы собираетесь получать ввод со стандартного устройства ввода, которым обычно является клавиатура. Если у вас еще нет опыта в программировании, возможно, вы не понимаете, что такое объект. Не беспокойтесь; классы и объекты будут рассматриваться в главе 7. А пока достаточно знать, что для получения ввода от пользователя достаточно включить в программу следующую команду:
Scanner reader = new Scanner(System.in);
Класс Scanner содержит несколько методов, которые могут использоваться для чтения ввода от пользователя. Чаще всего используются методы nextInt(), nextDouble() и nextLine() для чтения типов данных int, double и String соответственно.
Чтобы лучше понять, как работают эти методы, создайте в NetBeans новый проект с именем InputDemo. Если вы забыли, как создаются новые проекты в NetBeans, обращайтесь к разд. 2.2. Замените код приведенным ниже (номера строк не являются частью программы и приведены для удобства):
1 package inputdemo;
2 import java.util.Scanner;
3
4 public class InputDemo {
5 public static void main(String[] args) {
6 Scanner input = new Scanner(System.in);
7
8 System.out.print("Enter an integer: ");
9 int myInt = input.nextInt();
10 System.out.printf("You entered %d.%n%n",
myInt);
11
12 System.out.print("Enter a double: ");
13 double myDouble = input.nextDouble();
14 System.out.printf("You entered %.2f.%n%n",
myDouble);
15
16 System.out.print("Enter a string: ");
17 input.nextLine();
18 String myString = input.nextLine();
19 System.out.printf("You entered
\"%s\".%n%n", myString);
20
21 }
22 }
В строке 2 импортируется класс java.util.Scanner.
Затем в строке 6 создается объект Scanner, которому присваивается имя input.
В строке 8 пользователю предлагается ввести целое число. Затем программа читает целое число вызовом метода nextInt(). Наконец, в строке 10 данные, введенные пользователем, выводятся методом printf().
В строках 12–14 происходит нечто похожее, только на этот раз пользователю предлагается ввести значение double, а для чтения ввода используется метод nextDouble().
В строках 16–19 пользователю предлагается ввести строку, для чтения которой используется метод nextLine().
Впрочем, здесь можно заметить небольшое отличие. В строке 17 располагается дополнительная команда:
input.nextLine();
Иначе говоря, метод nextLine() вызывается дважды (в строках 17 и 18). Это необходимо из-за особенностей работы метода nextDouble() в строке 13. Метод nextDouble() ограничивается чтением double. Но когда пользователь вводит число, он также нажимает клавишу Enter. Клавиша Enter по сути вводит символ новой строки ("\n"), который игнорируется методом nextDouble, так как он не является частью double. Говорят, что метод nextDouble()не поглощает символ новой строки. Для поглощения этого символа необходим вызов метода nextLine() в строке 17.
Если удалить строку 17 и снова запустить программу, вы увидите, что у вас не будет возможности ввести строку. Дело в том, что метод nextLine() в строке 18 поглощает предыдущий символ новой строки. А поскольку после этого нет другой команды nextLine(), программа не будет ожидать другого ввода от пользователя.
Каждый раз, когда вы используете метод nextLine() после метода nextDouble(), всегда следует вставлять дополнительный метод nextLine() для потребления предыдущего символа новой строки.
То же относится к методу nextInt(). Попробуйте выполнить эту программу и ввести целое число, double и строку по запросам. Программа должна работать так, как ожидалось.
Кроме трех методов, упоминавшихся выше, в Java также существуют методы nextByte(), nextShort(), nextLong(), nextFloat() и nextBoolean() для чтения значений byte, short, long, float и boolean соответственно.
Каждый из этих методов рассчитан на чтение значений правильного типа данных. Например, метод nextDouble() ожидает получить double. Если пользователь не вводит значение правильного типа данных, метод попытается преобразовать ввод к правильному типу. Если попытка завершится неудачей, то метод сообщает об ошибке.
Например, если метод nextDouble() прочитает значение 20, то оно будет преобразовано в double. Но если метод прочитает строку «hello», будет выдано сообщение об ошибке.
О том, что делать при возникновении таких ошибок, будет рассказано в следующей главе.
6. Управляющие команды
В предыдущих главах вы узнали много нового. К настоящему моменту вы должны знать базовую структуру программ Java, а также уметь писать простые программы Java с использованием переменных. Кроме того, вы также научились пользоваться различными встроенными методами Java для взаимодействия с пользователями.
В этой главе будет сделан следующий шаг — вы узнаете, как управлять последовательностью выполнения команд вашей программы. По умолчанию команды выполняются по порядку, от начала программы к концу, в порядке их следования. Тем не менее такой порядок можно изменить с помощью управляющих команд.
К этой категории относятся команды принятия решений (if, switch), команды циклов (for, while, do-while) и команды перехода (break, continue). Все эти команды будут рассмотрены в следующих разделах.
Но сначала рассмотрим операторы сравнения.
6.1. Операторы сравнения
Многие управляющие команды используют некоторую разновидность сравнения. Программа выбирает тот или иной путь выполнения в зависимости от результата сравнения.
Из всех операторов сравнения чаще всего используется оператор проверки равенства. Если вы хотите узнать, равны ли две переменные, используйте оператор == (два знака =). Например, включая в программу выражение x == y, вы приказываете программе проверить, равно ли значение x значению y. Если они равны, значит, условие выполнено и команда дает результат true. В противном случае будет получен результат false.
Кроме проверки двух значений на равенство существуют и другие операторы сравнения, которые могут использоваться в управляющих командах.
Не равно (!=)
Возвращает true, если левая часть не равна правой.
5 != 2 true
6 != 6 false
Больше (>)
Возвращает true, если левая часть больше правой.
5 > 2 true
3 > 6 false
Меньше (<)
Возвращает true, если левая часть меньше правой.
1 < 7 true
9 < 6 false
Больше или равно (>=)
Возвращает true, если левая часть больше или равна правой.
5 >= 2 true
5 >= 5 true
3 >= 6 false
Меньше или равно (<=)
Возвращает true, если левая часть меньше или равна правой.
5 <= 7 true
7 <= 7 true
9 <= 6 false
Также существуют два логических оператора (&&, ||), которые пригодятся для объединения нескольких условий.
Оператор AND (&&)
Возвращает true, если выполняются все условия.
5==5 && 2>1 && 3!=7 true
5==5 && 2<1 && 3!=7 false, так как второе условие
(2<1) дает false
Оператор OR (||)
Возвращает true, если выполняется хотя бы одно условие.
5==5 || 2<1 || 3==7 true, так как первое условие
(5==5) дает true
5==6 || 2<1 || 3==7 false, так как все условия дают
false
6.2. Команды принятия решений
После знакомства с операторами сравнения посмотрим, как пользоваться этими операторами для управления последовательностью выполнения программы. Начнем с команды if.
6.2.1. Команда if
Команда if — одна из наиболее часто используемых команд управления последовательностью выполнения. Она позволяет программе проверить некоторое условие и выполнить соответствующее действие в зависимости от результата проверки.
Команда if имеет следующую структуру (номера строк добавлены для удобства):
1 if (выполняется условие 1)
2 {
3 действие A
4 }
5 else if (выполняется условие 2)
6 {
7 действие B
8 }
9 else if (выполняется условие 3)
10 {
11 действие C
12 }
13 else
14 {
15 действие D
16 }
В строке 1 проверяется первое условие. Если оно выполняется, то выполняются все команды, заключенные в фигурные скобки (строки 2–4). Остальная часть команды if (строки 5–16) пропускается.
Если первое условие не выполнено, то блоки else if, следующие за условием, могут использоваться для проверки дополнительных условий (строки 5–12). Блоков else if может быть несколько. Наконец, блок else (строки 13–116) может использоваться для выполнения кода в том случае, если не выполнено ни одно из предшествующих условий. Блоки else if и else обязательными не являются. Если нет дополнительных условий, которые требуется проверить, включать их не обязательно.
Чтобы лучше понять, как работает эта команда if, рассмотрим пример. Запустите NetBeans и создайте проект с именем IfDemo. Замените сгенерированный код следующим:
package ifdemo;
import java.util.Scanner;
public class IfDemo{
public static void main(String[] arg)
{
Scanner input = new Scanner(System.in);
System.out.print("\nPlease enter your age: ");
int userAge = input.nextInt();
if (userAge < 0 || userAge > 100)
{
System.out.println("Invalid Age");
System.out.println("Age must be between 0 and
100");
}
else if (userAge < 18)
System.out.println("Sorry you are underage");
else if (userAge < 21)
System.out.println("You need parental consent");
else
{
System.out.println("Congratulations!");
System.out.println("You may sign up for the
event!");
}
}
}
Программа запрашивает у пользователя его возраст и сохраняет результат в переменной userAge. Команда
if (userAge < 0 || userAge > 100)
проверяет, не будет ли значение userAge меньше нуля или больше 100. Если хотя бы одно из этих условий истинно, то программа выполняет все команды в следующей паре фигурных скобок. В данном примере будет выведена строка «Invalid Age», за которой следует сообщение «Age must be between 0 and 100».
С другой стороны, если оба условия ложны, то программа проверяет следующее условие — else if (userAge < 18). Если userAge меньше 18 (но больше либо равно 0, потому что первое условие не выполняется), программа выведет сообщение «Sorry you are underage».
Возможно, вы заметили, что команда:
System.out.println("Sorry you are underage");
не заключена в фигурные скобки. Дело в том, что фигурные скобки не обязательны, если выполняется только одна команда.
Если пользователь не ввел значение, меньшее 18, но введенное значение больше либо равно 18, но меньше 21, будет выполнена следующая команда else if. В этом случае выводится сообщение «You need parental consent».
Наконец, если введенное значение больше или равно 21, но меньше или равно 100, то программа выполнит код в блоке else. В этом случае будет выведена строка «Congratulations», за которой следует сообщение «You may sign up for the event!».
Запустите программу пять раз и введите при каждом запуске значение -1, 8, 20, 23 и 121 соответственно. Вы получите следующий результат:
Please enter your age: -1
Invalid Age
Age must be between 0 and 100
Please enter your age: 8
Sorry you are underage
Please enter your age: 20
You need parental consent
Please enter your age: 23
Congratulations!
You may sign up for the event!
Please enter your age: 121
Invalid Age
Age must be between 0 and 100
6.2.2. Тернарный оператор
Тернарный оператор (?) представляет собой упрощенную форму команды if, которая очень удобна для присваивания значения переменной в зависимости от результата условия. Синтаксис выглядит так:
условие?значение для true:значение для false;
Например, команда
3>2 ? 10 : 5;
вернет значение 10, так как 3 больше 2 (т. е. если условие 3 > 2 истинно). Затем это значение можно присвоить переменной.
Если же выполнить команду
int myNum = 3>2 ? 10 : 5;
myNum будет присвоено значение 10.
6.2.3. Команда switch
Команда switch похожа на команду if, не считая того, что она не работает с диапазонами значений. Команда switch требует, чтобы каждый случай базировался на одном значении.
Программа выполняет один из нескольких блоков кода в зависимости от значения переменной, используемой для выбора.
Команда switch имеет следующий синтаксис:
switch (переменная для выбора)
{
case первыйСлучай:
действие A;
break;
case второйСлучай:
действие B;
break;
default:
действие C;
break;
}
Количество случаев в команде switch не ограничено. Блок default не является обязательным; он выполняется, если ни один случай не подходит. Для выбора может использоваться переменная типа byte, short, char или int. Начиная с Java 7 для выбора также может использоваться переменная String.
Если некоторый случай подходит, то выполняются все команды, начиная со следующей строки, до команды break. Команда break приказывает программе выйти из команды switch и продолжить выполнение оставшейся части программы.
Рассмотрим пример использования команды switch. Чтобы опробовать этот пример, запустите NetBeans и создайте новый проект с именем SwitchDemo. Замените сгенерированный код следующим. В этом примере для выбора используется переменная типа String.
1 package switchdemo;
2 import java.util.Scanner;
3
4 public class SwitchDemo{
5
6 public static void main(String[] args) {
7
8 Scanner input = new Scanner(System.in);
9
10 System.out.print("Enter your grade: ");
11 String userGrade =
input.nextLine().toUpperCase();
12
13 switch (userGrade)
14 {
15 case "A+":
16 case "A":
17 System.out.println("Distinction");
18 break;
19 case "B":
20 System.out.println("B Grade");
21 break;
22 case "C":
23 System.out.println("C Grade");
24 break;
25 default:
26 System.out.println("Fail");
27 break;
28 }
29 }
30 }
Сначала программа предлагает пользователю ввести значение в строке 10. В строке 11 она читает введенные данные и сохраняет результат в userGrade следующей командой:
String userGrade = input.nextLine().toUpperCase();
Кому-то из читателей эта команда может показаться непривычной. Здесь мы вызываем два метода в одной команде:
input.nextLine()
Первый вызов читает данные, введенные пользователем. Метод возвращает строку. Полученная строка используется для вызова метода toUpperCase(). Эта команда показывает, как вызвать два метода в одной команде. Методы вызываются слева направо. Первым выполняется метод nextLine(), после чего следует вызов метода toUpperCase().
Пользовательский ввод необходимо преобразовать к верхнему регистру, прежде чем присваивать его userGrade, потому что в языке Java различается регистр символов. Программа должна выводить одно сообщение независимо от того, какой символ ввел пользователь: «A» или «a». Здесь любое значение, введенное в нижнем регистре, преобразуется к верхнему регистру перед присваиванием userGrade.
После получения введенного значения команда switch используется для определения выходного сообщения.
Если пользователь ввел «A+» (строка 15), то программа продолжает выполнение со следующей команды, пока не будет выполнена команда break. Это означает, что будут выполнены строки с 16-й по 18-ю. А это означает, что будет выведено сообщение «Distinction».
Если введено значение «A» (строка 16), программа выполняет строки 17 и 18. В этом случае также выводится сообщение «Distinction».
Если введенное значение не равно «A+» или «A», программа проверяет следующий случай. Проверки продолжаются в порядке сверху вниз, пока не будет найдет подходящий случай. Если не подходит ни один из случаев, отрабатывает блок default.
Выполнив приведенный выше код, вы получите следующий результат для указанных входных данных:
Enter your grade: A+
Distinction
Enter your grade: A
Distinction
Enter your grade: B
B Grade
Enter your grade: C
C Grade
Enter your grade: D
Fail
Enter your grade: Hello
Fail
6.3. Циклы
Перейдем к циклическим командам Java. В Java чаще всего используются четыре разновидности циклов: команда for, расширенная команда for, команда while и команда do-while.
6.3.1. Команда for
Команда for многократно выполняет блок кода, пока заданное условие продолжает действовать.
Синтаксис команды for:
for (исходное значение; условие; изменение значения)
{
// Некоторые действия
}
Чтобы понять, как работает команда for, рассмотрим следующий пример:
1 for (int i = 0; i < 5; i++)
2 {
3 System.out.println(i);
4 }
Наибольший интерес представляет команда 1:
for (int i = 0; i < 5; i++)
Она состоит из трех частей, разделенных символом ; (точка с запятой).
Первая часть объявляет и инициализирует переменную i типа int нулем. Переменная служит счетчиком цикла.
Вторая часть проверяет, что i меньше 5. Если это так, то будут выполнены команды в фигурных скобках. В данном примере фигурные скобки не обязательны, так как выполняется только одна команда.
После выполнения команды System.out.println(i) программа возвращается к последнему сегменту строки 1. Выражение i++ увеличивает значение i на 1. Таким образом, i меняет значение с 0 на 1.
После увеличения программа проверяет, что новое значение i все еще меньше 5. Если проверка дает положительный результат, команда System.out.println(i) выполняется снова.
Процесс проверки и увеличения счетчика цикла повторяется до тех пор, пока условие i < 5 не перестанет выполняться. В этот момент программа выходит из команды for и продолжает выполнять команды, следующие за циклом.
Результат этого фрагмента выглядит так:
0
1
2
3
4
Вывод останавливается на 4, потому что, когда переменная i станет равна 5, команда System.out.println(i) не будет выполнена, так как 5 не меньше 5.
Команда for обычно используется для перебора элементов массива. Например, если имеется массив
int[] myNumbers = {10, 20, 30, 40, 50};
команда for и поле length массива могут использоваться для перебора элементов так, как показано ниже:
for (int i = 0; i < myNumbers.length; i++)
{
System.out.println(myNumbers[i]);
}
Так как значение myNumbers.length равно 5, код выполняется от i = 0 до i = 4. Выполнив его, вы получите следующий результат:
10
20
30
40
50
6.3.2. Расширенная команда for
Кроме команды for при работе с массивами и коллекциями также может использоваться расширенный цикл for (коллекции будут рассматриваться в главе 9). Расширенный цикл for очень удобен для получения информации из массива без внесения в него каких-либо изменений.
Синтаксис расширенного цикла for:
for (объявление переменной : имя массива)
{
}
Допустим, имеется массив
int[] myNumbers = {10, 20, 30, 40, 50};
Для вывода элементов массива можно воспользоваться следующим кодом:
for (int item : myNumbers)
System.out.println(item);
В приведенном коде объявляется переменная item типа int, которая используется для перебора. При каждом выполнении цикла очередной элемент массива myNumbers присваивается переменной item. Например, при первом выполнении цикла item будет присвоено целое число 10.
В этом случае строка
System.out.println(item);
выводит 10.
При втором выполнении цикла item присваивается целое число 20. Строка
System.out.println(item);
выводит 20.
Цикл продолжается до тех пор, пока не будут выведены все элементы массива.
6.3.3. Команда while
Перейдем к циклам while. Команда while многократно выполняет инструкции, содержащиеся в цикле, пока некоторое условие не нарушается.
Структура цикла while:
while (условие истинно)
{
Действие A
}
В большинстве случаев при использовании команды while необходимо сначала объявить переменную, которая станет счетчиком цикла. Назовем эту переменную counter. Ниже приведен пример использования цикла while.
int counter = 5;
while (counter > 0)
{
System.out.println("Counter = " + counter);
counter = counter - 1;
}
При выполнении кода будет получен следующий результат:
Counter = 5
Counter = 4
Counter = 3
Counter = 2
Counter = 1
Команда while имеет относительно простой синтаксис. Команды в фигурных скобках выполняются, пока остается истинным условие counter > 0.
А вы заметили строку counter = counter — 1 в фигурных скобках? Эта строка критична. Она уменьшает значение counter на 1 при каждом выполнении цикла.
Значение counter должно уменьшаться на 1, чтобы условие (counter > 0) в какой-то момент стало равным false. Если вы забудете об этом, то цикл будет выполняться бесконечно. Она будет выводить сообщение counter = 5, пока вы каким-то образом не прервете ее выполнение. Не самая приятная перспектива, особенно если ваша программа велика и вы понятия не имеете, в каком сегменте кода произошло зацикливание.
6.3.4. Цикл do-while
Цикл do-while похож на цикл while с одним важным отличием — код в фигурных скобках цикла do-while гарантированно будет выполнен хотя бы один раз. Пример использования команды do-while:
int counter = 100;
do {
System.out.println("Counter = " + counter);
counter++;
} while (counter<0);
Условие (while (counter<0)) размещается после закрывающей фигурной скобки; это указывает на то, что оно будет проверяться после того, как код в фигурных скобках будет выполнен хотя бы один раз.
При выполнении этого кода вы получите следующий результат:
Counter = 100;
После того как команда System.out.println("Counter = " + counter); будет выполнена впервые, переменная counter увеличивается на 1. Значение counter становится равным 101. Когда программа достигает условия, оно оказывается ложным, так как counter не меньше 0. Затем программа выходит из цикла. При том что с исходным значением counter условие не выполняется (counter < 0), код в фигурных скобках все равно будет выполнен хотя бы один раз.
Обратите внимание: для команды do-while за условием должен следовать символ ; (точка с запятой).
6.4. Команды перехода
Мы рассмотрели большинство управляющих команд языка Java. Теперь рассмотрим команды перехода.
Командой перехода называется команда, которая приказывает программе продолжить выполнение с другой строки. Команды перехода часто используются в циклах и других управляющих конструкциях.
6.4.1. Команда break
Первая команда перехода — команда break. Вы уже видели, как использовать эту команду в конструкции switch. Кроме команд switch команда break также может использоваться в других управляющих командах. Она заставляет программу преждевременно выйти из цикла при выполнении некоторого условия. Рассмотрим пример использования команды break в команде for:
1 for ( int i = 0; i < 5; i++)
2 {
3 System.out.println("i = " + i);
4 if (i == 2)
5 break;
6 }
В этом примере команда if используется внутри цикла for. Подобное смешение различных управляющих команд в программировании встречается очень часто — например, команда while может быть заключена в команду if, или цикл for может быть заключен в команду while. Это называется вложением управляющих команд.
При выполнении приведенного выше фрагмента результат будет выглядеть так:
i = 0
i = 1
i = 2
А вы заметили, что цикл преждевременно завершается при i = 2?
Без команды break цикл должен выполняться от i = 0 до i = 4, потому что выполнением цикла управляет условие i < 5. Однако с командой break при i = 2 условие в строке 4 дает результат true. Команда break в строке 5 приводит к преждевременному завершению цикла.
6.4.2. Команда continue
Также в программах часто используется команда continue. При выполнении continue оставшаяся часть тела цикла пропускается для текущей итерации. С примером все станет яснее.
При выполнении следующего фрагмента кода
1 for (int i = 0; i<5; i++)
2 {
3 System.out.println("i = " + i);
4 if (i == 2)
5 continue;
6 System.out.println("I will not be printed if i=2.");
7 }
вы получите такой результат:
i = 0
I will not be printed if i=2.
i = 1
I will not be printed if i=2.
i = 2
i = 3
I will not be printed if i=2.
i = 4
I will not be printed if i=2.
При i = 2 строка после команды continue не выполняется. Программа возвращается к строке 1 и продолжает выполняться от этой точки. После этого возобновляется нормальное выполнение программы.
6.5. Обработка исключения
Итак, вы знаете, как управлять выполнением программы в «обычных» условиях с помощью управляющих команд. Теперь необходимо понять, как управлять последовательностью выполнения программы при возникновении ошибки. Такая ситуация называется обработкой исключений.
Когда вы пишете программу, всегда старайтесь упредить возможные ошибки. Если вы полагаете, что некоторый блок кода может вызвать ошибку, попробуйте обработать ее в команде try-catch-finally. Синтаксис команды try-catch-finally выглядит так:
try
{
действия
}
catch (тип ошибки)
{
действия при возникновении ошибки
}
finally
{
выполняется независимо от того, было ли выполнено условие try или catch.
}
Блоков catch может быть несколько. Кроме того, блок finally не является обязательным.
Рассмотрим пример. Запустите NetBeans и создайте новый проект с именем ErrorDemo. Замените сгенерированный код следующим:
1 package errordemo;
2 import java.util.Scanner;
3
4 public class ErrorDemo{
5 public static void main(String[] args) {
6
7 int num, deno;
8
9 Scanner input = new Scanner(System.in);
10
11 try
12 {
13 System.out.print("Please enter the
numerator: ");
14 num = input.nextInt();
15
16 System.out.print("Please enter the
denominator: ");
17 deno = input.nextInt();
18
19 System.out.println("The result is " +
num/deno);
20
21 }
22 catch (Exception e)
23 {
24 System.out.println(e.getMessage());
25 }
26 finally
27 {
28 System.out.println("---- End of Error
Handling Example ----");
29 }
30 }
31 }
В этом примере блок try располагается в строках 11–21, блок catch — в строках 22–25, а блок finally — в строках 26–29.
Если запустить код и ввести значения 12 и 4, вы получите сообщение
The result is 3
---- End of Error Handling Example ----
В этом случае программа пытается выполнить код в блоке try, и делает это успешно. В результате она выводит результат деления. После выполнения кода в блоке try выполняется код в блоке finally. Блок finally всегда выполняется независимо от того, был выполнен блок try или catch.
Снова запустите программу и введите числа 12 и 0. Вы получите сообщение:
/ by zero
---- End of Error Handling Example ----
В этом случае программа пытается выполнить код в блоке try, но сделать этого не может. Дело в том, что число невозможно разделить на 0. По этой причине вместо этого выполняется код в блоке catch. После выполнения блока catch также будет выполнен код в блоке finally.
Блок catch позволяет указать тип ошибки, которая должна перехватываться при выполнении. В нашем примере мы собираемся перехватывать «ошибку вообще». По этой причине используется запись
catch (Exception e)
где Exception — класс, к которому относится ошибка, а e — имя, присвоенное ошибке.
Exception — один из предварительно написанных классов в Java. Он обрабатывает все обобщенные ошибки и содержит метод с именем getMessage(), который возвращает описание причины исключения. Чтобы вывести сообщение об ошибке, используйте команду
System.out.println(e.getMessage());
В нашем примере будет выведено следующее сообщение об ошибке:
/ by zero
6.5.1. Конкретные ошибки
В приведенном выше примере класс Exception использовался для перехвата обобщенных ошибок. Кроме класса Exception в Java имеются другие классы для обработки более конкретных ошибок.
Эти классы очень полезны для решения конкретных задач, зависящих от перехваченной ошибки. Например, вы можете выводить собственные сообщения об ошибках.
Чтобы увидеть, как это работает, запустите NetBeans и создайте новый проект с именем ErrorDemo2. Замените сгенерированный код следующим:
package errordemo2;
import java.util.InputMismatchException;
import java.util.Scanner;
public class ErrorDemo2{
public static void main(String[] args) {
int choice = 0;
Scanner input = new Scanner(System.in);
int[] numbers = { 10, 11, 12, 13, 14, 15 };
System.out.print("Please enter the index of the
array: ");
try
{
choice = input.nextInt();
System.out.printf("numbers[%d] = %d%n",
choice, numbers[choice]);
}catch (ArrayIndexOutOfBoundsException e)
{
System.out.println("Error: Index is invalid.");
}catch (InputMismatchException e)
{
System.out.println("Error: You did not enter
an integer.");
}catch (Exception e)
{
System.out.printf(e.getMessage());
}
}
}
Если ввести значение
10
вы получите сообщение:
Error: Index is invalid.
Если же ввести строку
Hello
сообщение будет другим:
Error: You did not enter an integer.
Первая ошибка была представлена исключением ArrayIndexOutOfBoundsException, которое обрабатывается первым блоком catch. Исключение возникает при попытке обратиться к элементу с индексом, выходящим за границы.
Вторая ошибка InputMismatchException обрабатывается вторым блоком catch. Исключение InputMismatchException возникает тогда, когда метод Scanner не соответствует предполагаемому типу. В нашем примере input.nextInt() генерирует ошибку InputMismatchException, потому что ввод «Hello» не соответствует типу данных, на который рассчитан метод nextInt().
После двух конкретных блоков catch следует еще один блок catch для перехвата любых общих ошибок, которые не были предусмотрены предыдущими блоками.
В приведенном примере представлены всего три из множества исключений в Java.
Класс InputMismatchExpection находится в пакете java.util и должен быть импортирован перед использованием. С другой стороны, два других класса исключений (ArrayIndexOutOfBoundsException и Exception) находятся в пакете java.lang и импортируются по умолчанию всеми программами Java. Не беспокойтесь, если вы не помните, какие классы необходимо импортировать, а какие импортируются по умолчанию; NetBeans подскажет, нужно ли вам импортировать пакет или класс самостоятельно.
6.5.2. Выдача исключений
Теперь посмотрим, как выдавать исключения в программе. В предшествующем примере мы пытаемся перехватывать ошибки при заранее определенных условиях.
Например, ошибка ArrayIndexOutOfBoundsException перехватывается тогда, когда пользователь пытается обратиться к элементу, индекс которого выходит за границы массива. В этом примере это происходит тогда, когда пользователь вводит отрицательное или положительное число, большее 5.
Кроме перехвата ошибок при заранее определенных условиях вы также можете определять собственные условия, при которых должна происходить ошибка.
Допустим, по какой-то причине вы не хотите, чтобы пользователи обращались к первому элементу массива. Для этого можно потребовать, чтобы программа выдавала исключение, когда пользователь вводит значение 0.
Чтобы понять, как работает эта возможность, попробуйте выполнить предыдущую программу и введите значение 0. Программа выполняется нормально и выдает результат
numbers[0] = 10
Теперь попробуйте добавить команды
if (choice == 0)
throw new ArrayIndexOutOfBoundsException();
после команды
choice = input.nextInt();
в блоке try. Снова запустите программу и введите значение 0. На этот раз будет выполнен блок
catch(ArrayIndexOutOfBoundsException e)
Это объясняется тем, что, когда пользователи вводят значение 0, условие choice == 0 дает результат true. Следовательно, будет выполнена команда
throw new ArrayIndexOutOfBoundsException();
Из-за этой команды будет выполнен блок
catch(ArrayIndexOutOfBoundsException e)
