Читать книгу Java за неделю. Вводный курс - Валерий Станиславович Яценков - Страница 10
Часть I. Теория
Глава 3. Переменные и операторы
3.1 Переменные и типы данных
ОглавлениеМожет показаться, что в программировании нет ничего проще, чем переменная. Какие могут быть сложности? Тем не менее, для начинающих программистов сложности есть. Неправильное понимание того, как устроен мир переменных и данных, может привести к появлению трудно локализуемых ошибок.
Переменная представляет собой указатель на физическую область памяти, в которой хранятся данные. При помощи указателя мы можем записывать значения в память и считывать их оттуда. Иными словами, переменная – это имя фрагмента памяти компьютера. Размер этого фрагмента зависит от того, какие данные мы собираемся хранить.
Разрабатывая или запуская программу, мы не знаем заранее, по каким физическим адресам будут находиться данные в конкретном компьютере. Более того, в компиляторах современных языков принимаются специальные меры для дополнительного сокрытия информации о физическом размещении данных. Это делается для того, чтобы злоумышленнику было труднее получить доступ к критически важным данным, анализируя содержимое оперативной памяти компьютера.
До первого обращения к переменной ее надо объявить. При объявлении переменной указывают ее тип и имя. Это важно, потому что компилятор должен заранее знать, какой объем памяти выделить для хранения переменной, и как истолковывать данные, прочитанные из памяти.
Типы данных в языке Java можно разделить на две основные категории: примитивные (простые) и ссылочные. Они различаются по способу размещения данных в памяти.
Данные примитивного типа хранятся непосредственно в той ячейке памяти, которая ассоциирована с именем переменной. Обращаясь к переменной по имени, мы тем самым, обращаемся к данным в памяти. Если вы сравниваете две переменных, то сравниваются данные, которые с ними связаны. Если вы присваиваете одной переменной примитивного типа значение другой переменной примитивного типа, то происходит копирование данных.
В случае использования ссылочного типа в ячейке памяти, которая ассоциирована с именем переменной, хранится адрес данных, т.е. ссылка на данные, а не сами данные.
Необходимость в ссылочном типе данных можно продемонстрировать с помощью простого примера. Допустим, вы объявили строковую переменную с начальным значением «Java». Под это значение выделяется место в памяти. В процессе работы программы этой переменной присваивается новое значение «Hello, World!». Очевидно, что это совершенно другой объем данных, который не поместится в ранее отведенном фрагменте памяти.
Иными словами, ссылочные типы предназначены для работы с динамически создаваемыми и уничтожаемыми сущностями, объем которых невозможно предсказать заранее.
В таком случае программа размещает новые данные в другом фрагменте памяти. Новый адрес этих данных записывается в ячейку, которая ассоциирована с переменной ссылочного типа. Если на старые данные больше не ссылается ни одна переменная, то они превращаются в мусор (garbage) и удаляются из памяти при помощи специального сборщика мусора (garbage collector). В языке Java сборка мусора выполняется автоматически.
При проверке ссылочных переменных на равенство сравниваются не сами данные, а их адреса, хранящиеся в ссылочных переменных. Если вы присваиваете одной ссылочной переменной значение другой ссылочной переменной, при этом копируется адрес данных, а не сами данные.
3.1.1 Примитивные типы данных
В языке Java заявлено восемь примитивных типов данных. Первые четыре используются для хранения целых чисел.
byte – однобайтное целое – предназначен для хранения целых чисел в диапазоне от -128 до 127 и занимает один байт в памяти.
short – короткое целое – занимает два байта в памяти и применяется для хранения чисел в диапазоне от -32768 до32767.
int – целое – занимает 4 байта в памяти и применяется для хранения чисел в диапазоне от -231 (-2147483648) до 231—1 (2147483647). Это стандартный тип данных для работы с целыми числами.
При работе с числовыми данными старайтесь использовать тип int. Это связано с особенностями автоматического приведения типов, а также с тем, что целочисленные литералы (например, 10 или 123) в коде программы обрабатываются компилятором, как тип int. Приведение типов мы обсудим далее в этой главе.
long – длинное целое – занимает 8 байтов в памяти и хранит числа в диапазоне от -263 до 263—1. На практике настолько большие числа встречаются редко. Чтобы определить длинное целое число, следует добавить суффикс «L» в конце, например 5201225834L.
В дополнение к целочисленным типам, имеется два типа данных для хранения чисел с плавающей точкой.
float – с плавающей точкой – занимает 4 байта в памяти и может хранить числа в диапазоне от -3,4×1038 до 3,4×1038 с дискретностью 3,4×10—38. Такая точность представления соответствует 7 знакам после запятой. Если вы попытаетесь сохранить в типе float число 1,234567891 (10 знаков), оно будет округлено до 1,234568 (7 знаков).
Что такое дискретность? Вы не можете задать значение типа float с произвольной точностью. Ведь количество байт памяти для хранения этого числа ограничено. Если мы начнем перечислять подряд, начиная с ноля, числа с плавающей точкой, то они будут следовать с некоторым шагом (дискретностью) в младших разрядах: 0; 3,4×10—38; 6,8×10—38 и т. д. Величину дискретности можно условно назвать погрешностью представления числа. Для достижения более высокой точности применяется тип double.
double – с плавающей точкой, двойной точности – занимает 8 байтов в памяти и может хранить числа в диапазоне от -1,7×10308 до 1,7×10308 с дискретностью 1,7×10—308. Если вы не скованы ограничениями объема памяти, используйте тип double вместо float, как более точный.
По умолчанию, как только вы использовали десятичную точку в программе на языке Java, этому значению присваивается тип double. Если вы хотите, чтобы это число было истолковано именно как float, добавьте суффикс «F» в конце числа.
Кроме шести перечисленных типов, Java располагает двумя специфическими типами данных.
char – символ – занимает 2 байта и применяется для хранения одиночного символа Unicode, например «A», "@», «$» и т. д.
boolean – логический – это особый тип данных, который может хранить только два фиксированных значения: true (истина) и false (ложь). Размер занятой памяти зависит от реализации Java—машины. Этот тип данных широко используется в условных операторах и операторах цикла, которые мы рассмотрим позже.
Все остальные типы данных, включая пользовательские типы, являются ссылочными.
3.1.2 Объявление и инициализация переменных
При объявлении переменной указывается тип переменной и ее имя. Переменная может быть объявлена в любом месте программы, главное – до первого использования.
boolean fileSaved;
Если объявляется несколько переменных одного типа, то их можно перечислить через запятую.
int userNum, userAge, userWeight;
Одновременно с объявлением переменной ей можно присвоить значение. Эта процедура называется инициализацией.
int start=10, end=100;
Допускается динамическая инициализация переменной, когда ей присваивается значение, полученное вычислением из значений других переменных. Исходные переменные должны быть объявлены и инициализированы ранее.
int start=5, end=10;
int sum=a+b;
В этом примере переменная sum инициализирована значением 15.
Обратите внимание, что в момент динамической инициализации не возникает связь между переменными. Например, если после инициализации изменится значение переменных start и end, это никак не повлияет на значение sum.
3.1.3 Доступность переменных
Доступность, или область видимости переменных – это важный аспект программирования. Если кратко, переменная доступна внутри блока, определенного парой фигурных скобок, внутри которого она объявлена. Например, если переменная объявлена внутри цикла, то она будет доступна только внутри этого цикла. Снаружи цикла может быть объявлена переменная с таким же именем, но фактически это будет совершенно другая переменная.
Допустим, в некой фирме работает Иванов, он выполняет свои задачи в пределах штата фирмы. В соседнем офисе тоже работает Иванов, но это другой человек, который делает другую работу. Директор первой фирмы не может отдавать распоряжения Иванову из второй фирмы. Для него второй Иванов недоступен.
Если переменная доступна только внутри некого метода (функции), то она называется локальной. Если переменная задана на уровне класса, она называется глобальной. Глобальные переменные обычно доступны любому из методов, входящих в класс. При использовании глобальных переменных необходимо соблюдать осторожность. Если внутри одного из методов случайно изменить значение глобальной переменной, другие методы будут получать неправильное значение. Это приведет к появлению трудно локализуемой логической ошибки, на которую не реагирует компилятор.
3.1.4 Ввод и считывание данных
Переменным можно присваивать значения, введенные извне. Давайте немного отвлечемся от абстрактных рассуждений и запустим две простых программы, которые запрашивают данные у пользователя и обрабатывают их. К этому моменту вы должны уметь создавать проекты в среде NetBeans IDE, поэтому я привожу только исходный код примеров.
Программа из листинга 3.1 поддерживает консольный ввод – пользователь читает запросы программы и вводит данные в окне системного монитора среды NetBeans. В программе из листинга 3.2 задействованы модальные окна с привычным графическим оформлением. Вы увидите, насколько просты эти программы. Не волнуйтесь, если что-то непонятно. Пока просто привыкайте к новым терминам. По мере чтения этой книги придет полное понимание.
Листинг 3.1 Чтение консольного ввода, вывод в консоль
import java.util.Scanner;
public class Listing3_1 {
public static void main (String [] args) {
// Создаем объект input класса Scanner
Scanner input = new Scanner(System.in);
// Переменная для хранения имени пользователя
String name;
// Переменная для хранения отчества пользователя
String surName;
// Переменная для хранения даты рождения пользователя
int yearBorn;
// Переменная для хранения текущего года
int yearNow;
// Выводим запрос данных
System.out.print («Ваше имя:»);
// Считываем имя (строка)
name = input.nextLine ();
System.out.print («Ваше отчество:»);
// Считываем отчество (строка)
surName = input.nextLine ();
System.out.print («Какой сейчас год?»);
// Считываем текущий год (целое число)
yearNow = input.nextInt ();
System.out.print («В каком году вы родились?»);
// Считываем год рождения (целое число)
yearBorn = input.nextInt ();
System.out.println («Здравствуйте, "+name+" "+surName+»!»);
System.out.println («Ваш возраст: "+ (yearNow-yearBorn) +».»);
}
}
В первой строке этой программы мы импортируем класс Scanner, который входит в состав системного пакета java. util. Затем мы создаем новый объект класса Scanner и назначаем ему идентификатор (имя) input. После этого приступаем к получению данных от пользователя. Выводим в консоль текстовый запрос и считываем ответ. Обратите внимание, что текстовые ответы мы считываем при помощи метода nextLine (), а целочисленные при помощи метода nextInt (). В противном случае возникнет ошибка несоответствия типа данных. Ведь мы объявили переменные yearNow и yearBorn как целые числа.
Отдельно разберем строку
System.out.println («Ваш возраст: "+ (yearNow-yearBorn) +».»);
В этой строке происходит арифметическое вычисление возраста пользователя, формирование строки вывода и вывод в консоль. Выражение (yearNow-yearBorn) обязательно должно быть в круглых скобках, потому что сначала должно быть вычислено его значение, а затем результат вычисления будет преобразован из числа в строку (автоматическое приведение типов).
Наберите или скачайте исходный код программы и запустите проект на выполнение. Введите ответы на вопросы. В окно терминала должно быть выведено что-то наподобие этого:
run:
Ваше имя: Иван
Ваше отчество: Петрович
Какой сейчас год? 2018
В каком году вы родились? 1988
Здравствуйте, Иван Петрович!
Ваш возраст: 30.
СБОРКА УСПЕШНО ЗАВЕРШЕНА (общее время: 22 секунды)
На компьютере с ОС Windows вместо символов кириллицы вы можете увидеть квадратики. В этом случае необходимо настроить кодировку проекта. В окне просмотра содержимого проекта щелкните правой кнопкой мыши на названии проекта и выберите пункт Свойства контекстного меню. В открывшемся окне найдите поле «Кодировка» и выберите в списке кодировку windows—1251. Нажмите OK.
Вторая программа имеет графический интерфейс, основанный на модальных окнах. Это специальные окна, которые содержат сообщение или поле ввода. Чтобы программа продолжила выполнение, пользователь обязательно должен отреагировать на появление окна – ввести данные или прочитать сообщение и закрыть.
Листинг 3.2 Ввод и вывод данных в модальных окнах
// импортируем класс JOptionPane из библиотеки Swing
import javax.swing.JOptionPane;
public class Listing3_2 {
public static void main (String [] args) {
// Объявление числовых переменных
int yearNow, yearBorn, userAge;
// Объявление строковой переменной
String userData;
// Выводим окно запроса текущей даты
userData = JOptionPane.showInputDialog («Какой сейчас год?»);
// Преобразуем строку в число в явном виде
yearNow = Integer.parseInt (userData);
// Выводим окно запроса года рождения
userData = JOptionPane.showInputDialog («В каком году вы родились?»);
// Преобразуем строку в число в явном виде
yearBorn = Integer.parseInt (userData);
// Вычисляем возраст
userAge = yearNow – yearBorn;
// Выводим окно сообщения с результатом
JOptionPane.showMessageDialog (null, «Ваш возраст: " + userAge);
}
}
В первой строке программы мы импортируем класс JOptionPane из библиотеки Swing. Библиотека Swing содержит набор классов для разработки приложений с графическим интерфейсом. Это очень емкая и мощная библиотека, входящая в пакет поставки SDK. Вы будете постоянно использовать ее при разработке приложений с графическим интерфейсом. Класс JOptionPane предназначен для создания стандартных модальных (диалоговых) окон. Для вывода окна с запросом данных применяется метод showInputDialog (), а для вывода сообщения – метод showMessageDialog ().
Любые значения, возвращаемые методом showInputDialog () являются строковыми данными. Чтобы выполнить над ними арифметические действия, необходимо в явном виде преобразовать строки в числа. Мы делаем это при помощи метода parseInt () системного класса Integer:
yearNow = Integer.parseInt (userData);
Программа завершается вычислением возраста пользователя и выводом результата.
Запустите проект на выполнение. Вы должны поочередно увидеть три диалоговых окна (рис. 3.1).
Рис.3.1 Диалоговые окна запроса и вывода данных
Если все работает правильно, нажмите клавишу F11 или выберите пункт меню Выполнить | Собрать проект. Будет создан исполняемый файл приложения. Его можно запустить на любом компьютере, где установлена Java-машина. Оформление окон приложения – цветовая схема, форма кнопок – может различаться в зависимости от операционной системы и реализации Java-машины.
По умолчанию файл проекта находится в папке Документы | NetBeansProjects. Внутри папки с именем проекта найдите папку dist. В этой папке находится готовый распространяемый файл приложения с расширением jar.