FirstObject.jumpMustang();
Вывод строки "Мустанг уже здесь!" в примере осуществляет метод println()(ln– переход к новой строке после вывода) свойства out класса System, который подключается к приложению автоматически вместе с пакетом java.lang. Приведенную программу необходимо поместить в файл FirstProgram.java (расширение .java обязательно), имя которого совпадает с именем класса.
Объявление классов предваряет строка
Package chapt01;
указывающая на принадлежность классов пакету с именем chapt01, который является на самом деле каталогом на диске. Для приложения, состоящего из двух классов, наличие пакетов не является необходимостью. При отсутствии слова package классы будут отнесены к пакету по умолчанию, размещенному в корне проекта. Если же приложение состоит из нескольких сотен классов (вполне обычная ситуация), то размещение классов по пакетам является жизненной необходимостью.
Классы из примеров 2 и 3 сохраняются в файлах FirstProgram.java. На практике рекомендуется хранить классы в отдельных файлах.
Простейший способ компиляции – вызов строчного компилятора из корневого каталога (в нем находится каталог chapt01):
javac chapt01/FirstProgram.java
При успешной компиляции создаются файлы FirstProgram.class
и Mustang.class. Запустить этот виртуальный код можно с помощью интерпретатора Java:
Java chapt01.FirstProgram
Здесь к имени приложенияFirstProgram.class добавляется имя пакета chapt01, в котором он расположен.
Чтобы выполнить приложение, необходимо загрузить и установить последнюю версию пакета, например с сайта java.sun.com. При инсталляции рекомендуется указывать для размещения корневой каталог. Если JDK установлена
в директории (для Windows) c:\jdk1.6.0, то каталог, который компилятор Java будет рассматривать как корневой для иерархии пакетов, можно вручную задавать с помощью переменной среды окружения СLASSPATHв виде:
СLASSPATH=.;c:\jdk1.6.0\
Переменной задано еще одно значение ‘.’ для использования текущей директории, например с:\temp, в качестве рабочей для хранения своих собственных приложений.
Чтобы можно было вызывать сам компилятор и другие исполняемые программы, переменную PATH нужно проинициализировать в виде
PATH=c:\jdk1.6.0\bin
Этот путь указывает на месторасположение файлов javac.exe и java.exe. В различных версиях операционных систем путь к JDK может указываться различными способами.
Однако при одновременном использовании нескольких различных версий компилятора и различных библиотек применение переменных среды окружения начинает мешать эффективной работе, так как при выполнении приложения поиск класса осуществляется независимо от версии. Когда виртуальная машина обнаруживает класс с подходящим именем, она его и подгружает. Такая ситуация предрасполагает к ошибкам, порой трудноопределимым. Поэтому переменные окружения лучше не определять вовсе.
Далее программа отображает в окне консоли аргументы командной строки метода main(). Аргументы представляют последовательность строк, разделенных пробелами, значения которых присваиваются объектам массива String[] args. Объекту args[0] присваивается значение первой строки и т.д. Количество аргументов определяется значением args.length.
/* пример # 4 : вывод аргументов командной строки: OutArgs.java */
package chapt01;
public class OutArgs {
public static void main(String[] args) {
for(String str : args)
System.out.printf("Aрг-> %s%n", str);
}
}
В данном примере используется новый вид цикла версии Java 5.0 forязыка Java и метод форматированного вывода printf(). Тот же результат был бы получен при использовании традиционного цикла
for (int i = 0; i < args.length; i++)
System.out.println("Aрг-> " + args[i]);
Запуск этого приложения осуществляется с помощью следующей командной строки вида:
java chapt01.OutArgs 2007 Mustang "Java SE 6"
что приведет к выводу на консоль следующей информации:
Арг-> 2007
Арг-> Mustang
Арг-> Java SE 6
Приложение, запускаемое с аргументами командной строки, может быть использовано как один из способов ввода строковых данных.
Классы и объекты
Классы в языке Java объединяют поля класса, методы, конструкторы и логические блоки. Основные отличия от классов C++: все функции определяются внутри классов и называются методами; невозможно создать метод, не являющийся методом класса, или объявить метод вне класса; ключевое слово inline, как в C++ , не поддерживается; спецификаторы доступа public,private,protectedвоздействуют только на те объявления полей, методов и классов, перед которыми они стоят, а не на участок от одного до другого спецификатора, как в С++; элементы по умолчанию не устанавливаются в private, а доступны для классов из данного пакета. Объявление класса имеет вид:
[спецификаторы] class ИмяКласса [extends СуперКласс]
[implements список_интерфейсов]{/*определение класса*/}
Спецификатор доступа к классу может быть public (класс доступен в данном пакета и вне пакета), final (класс не может иметь подклассов), abstract (класс может содержать абстрактные методы, объект такого класса создать нельзя). По умолчанию спецификатор устанавливается в дружественный (friendly). Такой класс доступен только в текущем пакете. Спецификатор friendly при объявлении вообще не используется и не является ключевым словом языка. Это слово используется, чтобы как-то определить значение по умолчанию.
Любой класс может наследовать свойства и методы суперкласса, указанного после ключевого слова extends, и включать множество интерфейсов, перечисленных через запятую после ключевого слова implements. Интерфейсы представляют собой абстрактные классы, содержащие только нереализованные методы.
// пример # 5 : простой пример Java Beans класса: User.java
package chapt01;
public class User {
public int numericCode;//нарушение инкапсуляции
private String password;
public void setNumericCode(int value) {
if(value > 0) numericCode = value;
else numericCode = 1;
}
public int getNumericCode() {
return numericCode;
}
public void setPassword(String pass) {
if (pass != null) password = pass;
else password = "11111";
}
public String getPassword() {
//public String getPass() {//некорректно – неполное имя метода
return password;
}
}
Класс User содержит два поля numericCode и password, помеченные как public и private. Значение поля password можно изменять только при помощи методов, например setPassword(). Поле numericCode доступно непосредственно через объект класса User. Доступ к методам и public-полям данного класса осуществляется только после создания объекта данного класса.
/*пример # 6 : создание объекта, доступ к полям
и методам объекта: UserView.java : Runner.java */
package chapt01;
class UserView {
public static void welcome(User obj) {
System.out.printf("Привет! Введен код: %d, пароль: %s",
obj.getNumericCode(), obj.getPassword());
}
}
public class Runner {
public static void main(String[] args) {
User user = new User();
user.numericCode = 71;//некорректно - прямой доступ
// user.password = null; // поле недоступно
user.setPassword("pass"); //корректно
UserView.welcome(user);
}
}
Компиляция и выполнение данного кода приведут к выводу на консоль следующей информации:
Привет! Введен код: 71, пароль: pass
Объект класса создается за два шага. Сначала объявляется ссылка на объект класса. Затем с помощью оператора new создается экземпляр объекта, например:
User user; //объявление ссылки
user = newUser(); //создание объекта
Однако эти два действия обычно объединяют в одно:
User user = new User();/*объявление ссылки и создание объекта*/
Оператор newвызывает конструктор, поэтому в круглых скобках могут стоять аргументы, передаваемые конструктору. Операция присваивания для объектов означает, что две ссылки будут указывать на один и тот же участок памяти.
Сравнение объектов
Операции сравнения ссылок на объекты не имеют особого смысла, так как при этом сравниваются адреса. Для сравнения значений объектов необходимо использовать соответствующие методы, например, equals(). Этот метод наследуется в каждый класс из суперкласса Object, который лежит в корне дерева иерархии всех классов и переопределяется в произвольном классе для определения эквивалентности содержимого двух объектов этого класса.
/* пример # 7 : сравнение строк и объектов : ComparingStrings.java */
package chapt01;
public class ComparingStrings {
public static void main(String[] args) {
String s1, s2;
s1 = "Java";
s2 = s1; // переменная ссылается на ту же строку
System.out.println("сравнение ссылок "
+ (s1 == s2)); // результат true
// создание нового объекта добавлением символа
s1 += '2';
// s1-="a"; //ошибка, вычитать строки нельзя
// создание нового объекта копированием
s2 = new String(s1);
System.out.println("сравнение ссылок "
+ (s1 == s2)); // результат false
System.out.println("сравнение значений "
+ s1.equals(s2)); // результат true
}
}
Консоль
Взаимодействие с консолью с помощью потока System.in представляет собой один из простейших способов передачи информации в приложение. В следующем примере рассматривается ввод информации в виде символа из потока ввода, связанного с консолью, и последующего вывода на консоль символа и его числового кода.
// пример # 8 : чтение символа из потока System.in : DemoSystemIn.java
package chapt01;
public class ReadCharRunner {
public static void main(String[] args) {
int x;
try {
x = System.in.read();
char c = (char)x;
System.out.println("Код символа: " + c + " =" + x);
} catch (java.io.IOException e) {
e.printStackTrace();
}
}
}
Обработка исключительной ситуации IOException, которая возникает в операциях ввода/вывода и в любых других взаимодействиях с внешними устройствами, осуществляется в методе main() с помощью реализации блока
try-catch.
Ввод блока информации осуществляется с помощью чтения строки из консоли. Далее строка может быть использована в исходном виде или преобразована к требуемому виду.
// пример # 9 : чтение строки из консоли : ReadCharRunner.java
package chapt01;
import java.io.*;//подключение пакета классов
public class ReadCharRunner {
public static void main(String[] args) {
/* байтовый поток ввода System.in передается конструктору потока
чтения при создании объекта класса InputStreamReader */
InputStreamReader is =
new InputStreamReader(System.in);
/* производится буферизация данных, исключающая необходимость
обращения к источнику данных при выполнении операции чтения */
BufferedReader bis = new BufferedReader(is);
try {
System.out.println(
"Введите Ваше имя и нажмите <Enter>:");
/* чтение строки из буфера; метод readLine() требует обработки
возможной ошибки при вводе из консоли в блоке try */
String name = bis.readLine();
System.out.println("Привет, " + name);
} catch (IOException e) {
System.err.print("ошибка ввода " + e);
}
}
}
В результате запуска приложения будет выведено, например, следующее:
Введите Ваше имя и нажмите <Enter>:
Остап
Привет, Остап
Позже будут рассмотрены более удобные способы извлечения информации из потока ввода, в качестве которого может фигурировать не только консоль, но и дисковый файл, сокетное соединение и пр.
Кроме того, в шестой версии языка существует возможность поддержать национальный шрифт с помощью метода printf() определенного для класса Console.
/* пример # 10 : использование метода printf() класса Console: PrintDeutsch.java */
public class PrintDeutsch {
public static void main(String[] args) {
String str = "über";
System.out.println(str);
Console con = System.console();
con.printf("%s", str);
}
}
В результате будет выведено:
□ber
über
Простой апплет
Одной из целей создания языка Java было создание апплетов - небольших программ, запускаемых Web-браузером. Поскольку апплеты должны быть безопасными, они ограничены в своих возможностях, хотя остаются мощным инструментом поддержки Web-программирования на стороне клиента.
// пример # 11 : простой апплет: FirstApplet.java
importjava.awt.Graphics;
importjava.util.Calendar;
public class FirstApplet extendsjavax.swing.JApplet {
private Calendar calendar;
public void init() {
calendar = Calendar.getInstance();
setSize(250,80);
}
public void paint(Graphics g) {
g.drawString("Апплет запущен:", 20, 15);
g.drawString(
calendar.getTime().toString(), 20, 35);
}
}
Для вывода текущего времени и даты в этом примере был использован объект Calendar из пакета java.util. Метод toString() используется для преобразования информации, содержащейся в объекте, в строку для последующего вывода в апплет с помощью метода drawString(). Цифровые параметры этого метода обозначают горизонтальную и вертикальную координаты начала рисования строки, считая от левого верхнего угла апплета.
Апплету не нужен метод main()- код его запуска помещается в метод init()илиpaint(). Для запуска апплета нужно поместить ссылку на его класс в HTML-документ и просмотреть этот документ Web-браузером, поддерживающим Java. При этом можно обойтись очень простым фрагментом (тегом) <applet> в HTML-документе view.html:
<html><body>
<applet code= FirstApplet.class width=300 height=300>
</applet></body></html>
Сам файл FirstApplet.class при таком к нему обращении должен находиться в той же директории, что и HTML-документ. Исполнителем HTML-документа является браузер Microsoft Internet Explorer или какой-либо другой, поддерживающий Java.
Результат выполнения документа view.html изображен на рис.1.4.
Рис. 1.4. Запуск и выполнение апплета
Для запуска апплетов можно использовать также входящую в JDK программу
appletviewer.exe.
Задания к главе 1
Вариант A
1. Создать класс Hello, который будет приветствовать любого пользователя, используя командную строку.
2. Создать приложение, которое отображает в окне консоли аргументы командной строки метода main() в обратном порядке.
3. Создать приложение, выводящее n строк с переходом и без перехода на новую строку.
4. Создать приложение для ввода пароля из командной строки и сравнения его со строкой-образцом.
5. Создать программу ввода целых чисел как аргументов командной строки, подсчета их суммы (произведения) и вывода результата на консоль.
6. Создать приложение, выводящее фамилию разработчика, дату и время получения задания, а также дату и время сдачи задания. Для получения последней даты и времени использовать класс Calendar из пакета java.util.
7. Создать апплет на основе предыдущего задания и запустить его с помощью HTML-документа.
Вариант B
Ввести с консоли n целых чисел и поместить их в массив. На консоль вывести:
1. Четные и нечетные числа.
2. Наибольшее и наименьшее число.
3. Числа, которые делятся на 3 или на 9.
4. Числа, которые делятся на 5 и на 7.
5. Элементы, расположенные методом пузырька по убыванию модулей.
6. Все трехзначные числа, в десятичной записи которых нет одинаковых цифр.
7. Наибольший общий делитель и наименьшее общее кратное этих чисел.
8. Простые числа.
9. Отсортированные числа в порядке возрастания и убывания.
10. Числа в порядке убывания частоты встречаемости чисел.
11. “Счастливые” числа.
12. Числа Фибоначчи: f0 = f1 = 1, f (n) = f (n–1) + f (n–2).
13. Числа-палиндромы, значения которых в прямом и обратном порядке совпадают.
14. Элементы, которые равны полусумме соседних элементов.
15. Период десятичной дроби p = m/n для первых двух целых положительных чисел n и m, расположенных подряд.
16. Построить треугольник Паскаля для первого положительного числа.
Тестовые задания к главе 1
Вопрос 1.1.
Дан код:
class Quest1 {
private static void main (String a) {
System.out.println("Java 2");
} }
Какие исправления необходимо сделать, чтобы класс Quest1 стал запускаемым приложением? (выберите 2 правильных варианта)
1) объявить класс Quest1 как public;
2) заменить параметр метода main() на String[] a;
3) заменить модификатор доступа к методу main() наpublic;
4) убрать параметр из объявления метода main().
Вопрос 1.2.
Выберите истинные утверждения о возможностях языка Java: (выберите 2)
1) возможно использование оператора goto;
2) возможно создание метода, не принадлежащего ни одному классу;
3) поддерживается множественное наследование классов;
4) запрещена перегрузка операторов;
5) поддерживается многопоточность.
Вопрос 1.3.
Дан код:
class Quest3 {
public static voidmain(String s[ ]) {
String args;
System.out.print(args + s);
}
}
Результатом компиляции кода будет:
1) ошибка компиляции: метод main() содержит неправильное имя параметра;
2) ошибка компиляции: переменная args используется до инициализации;
3) ошибка компиляции: несовпадение типов параметров при вызове метода print();
4) компиляция без ошибок.
Вопрос 1.4.
Дан код:
public class Quest4 {
public static void main(String[] args) {
byteb[]=new byte[80];
for (int i=0;i<b.length;i++)
b[i]=(byte)System.in.read();
System.out.print(“Ok”);
} }
Результатом компиляции и запуска будет:
1) вывод: Ok;
2) ошибка компиляции, так как метод read() может порождать исключительную ситуацию типа IOException;
3) ошибка компиляции, так как длина массива b может не совпадать
с длиной считываемых данных;
4) ошибка времени выполнения, так как массив уже проинициализирован.
Вопрос 1.5.
Дан код:
public class Quest5{
public static void main(){
System.out.print("А"); }
public static void main(String args){
System.out.print("Б"); }
public static void main(String[] args){
System.out.print("В"); } }
Что будет выведено в результате компиляции и запуска:
1) ошибка компиляции;
2) Б;
3) ВБА;
4) В;
5) АБВ.
ТИПЫ ДАННЫХ И ОПЕРАТОРЫ
Любая программа манипулирует информацией (простыми данными и объектами) с помощью операторов. Каждый оператор производит результат из значений своих операндов или изменяет непосредственно значение операнда.
Базовые типы данных и литералы
В языке Java используются базовые типы данных, значения которых размещаются в стековой памяти (stack). Эти типы обеспечивают более высокую производительность вычислений по сравнению с объектами. Кроме этого, для каждого базового типа имеются классы-оболочки, которые инкапсулируют данные базовых типов в объекты, располагаемые в динамической памяти (heap).
Определено восемь базовых типов данных, размер каждого из которых остается неизменным независимо от платформы. Беззнаковых типов в Java не существует. Каждый тип данных определяет множество значений и их представление в памяти. Для каждого типа определен набор операций над его значениями.
Тип | Размер (бит) | По умолчанию | Значения (диапазон или максимум) |
boolean | false | true, false | |
byte | –128..127 | ||
char | '\u0000' | 0..65535 | |
short | –32768..32767 | ||
int | -2147483648..2147483647 | ||
long | 922372036854775807L | ||
float | 0.0 | 3.40282347E+38 | |
double | 0.0 | 1.797693134486231570E+308 |
В Java используются целочисленные литералы, например: 35– целое десятичное число, 071 – восьмеричное значение, 0х51 – шестнадцатеричное значение. Целочисленные литералы по умолчанию относятся к типу int. Если необходимо определить длинный литерал типа long, в конце указывается символ L (например: 0xffffL). Если значение числа больше значения, помещающегося в int (2147483647), то Java автоматически предполагает, что оно типа long. Литералы с плавающей точкой записываются в виде 1.618 или в экспоненциальной форме 0.112E-05 и относятся к типу double, таким образом, действительные числа относятся к типу double. Если необходимо определить литерал типа float, то в конце литерала следует добавить символ F. Символьные литералы определяются в апострофах ('a', '\n', '\141', '\u005a' ). Для размещения символов используется формат Unicode, в соответствии с которым для каждого символа отводится два байта. В формате Unicode первый байт содержит код управляющего символа или национального алфавита, а второй байт соответствует стандартному ASCII коду, как в C++. Любой символ можно представить в виде '\ucode', где code представляет двухбайтовый шестнадцатеричный код символа. Java поддерживает управляющие символы, не имеющие графического изображения;
'\n'– новая строка, '\r' – переход к началу, '\f' – новая страница, '\t'– табуляция, '\b' – возврат на один символ,'\uxxxx' – шестнадцатеричный символ Unicode, '\ddd'– восьмеричный символ и др. Начиная с J2SE 5.0 используется формат Unicode 4.0. Поддержку четырехбайтным символам обеспечивает наличие специальных методов в классе Character.
К литералам относятся булевские значения true и false, а также null – значение по умолчанию для ссылки на объект. При инициализации строки всегда создается объект класса String – это не массив символов и не строка. Строки, заключенные в двойные апострофы, считаются литералами и размещаются в пуле литералов, но в то же время такие строки представляют собой объекты.
В арифметических выражениях автоматически выполняются расширяющие преобразования типа byte à short à int à long à float à
double. Java автоматически расширяет тип каждого byte или short операнда до int в выражениях. Для сужающих преобразований необходимо производить явное преобразование вида (тип)значение. Например:
byte b = (byte)128; //преобразование int в byte
Указанное в данном примере преобразование необязательно, так как в операциях присваивания литералов при инициализации преобразования выполняются автоматически. При инициализации полей класса и локальных переменных с использованием арифметических операторов автоматически выполняется приведение литералов к объявленному типу без необходимости его явного указания, если только их значения находятся в допустимых пределах, кроме инициализации объектов классов-оболочек. Java не позволяет присваивать переменной значение более длинного типа, в этом случае необходимо явное преобразование типа. Исключение составляют операторы инкремента (++), декремента (--) и сокращенные операторы (+=, /= и т.д.). При явном преобразовании (тип)значение возможно усечение значения.
Имена переменных не могут начинаться с цифры, в именах не могут использоваться символы арифметических и логических операторов, а также символ ‘#’. Применение символов ‘$’ и ‘_’ допустимо, в том числе и в первой позиции имени.
/* пример # 1 : типы данных, литералы и операции над ними :TypeByte.java */
packagechapt02;
public class TypeByte {
public static void main(String[] args) {
byte b = 1, b1 = 1 + 2;
final byte B = 1 + 2;
//b = b1 + 1; //ошибка приведения типов
/* b1 – переменная, и на момент выполнения кода b = b1 + 1; может измениться, и выражение b1 + 1 может превысить допустимый размер byte- типа */
b = (byte)(b1 + 1);
b = B + 1; // работает
/* B - константа, ее значение определено, компилятор вычисляет значение выражения B + 1, и если его размер не превышает допустимого для byte типа, то ошибка не возникает */
//b = -b; //ошибка приведения типов
b = (byte) -b;
//b = +b; //ошибка приведения типов
b = (byte) +b;
int i = 3;
//b = i; //ошибка приведения типов, int больше чем byte
b = (byte) i;
final int I = 3;
b = I; // работает
/*I –константа. Компилятор проверяет, не превышает ли ее значение допустимый размер для типа byte, если не превышает, то ошибка не возникает */
final int I2 = 129;
//b=I2; //ошибка приведения типов, т.к. 129 больше, чем 127
b = (byte) I2;
b += i++; // работает
b += 1000; // работает
b1 *= 2; // работает
float f = 1.1f;
b /= f; // работает
/* все сокращенные операторы автоматически преобразуют результат выражения к типу переменной, которой присваивается это значение. Например, b /= f; равносильно b = (byte)(b / f); */
}
}
Переменная базового типа, объявленная как член класса, хранит нулевое значение, соответствующее своему типу. Если переменная объявлена как локальная переменная в методе, то перед использованием она обязательно должна быть проинициализирована, так как она не инициализируется по умолчанию нулем. Область действия и время жизни такой переменной ограничена блоком {}, в котором она объявлена.
Документирование кода
В языке Java используются блочные и однострочные комментарии /* */
и //, аналогичные комментариям, применяемым в C++. Введен также новый вид комментария /** */, который может содержать дескрипторы вида:
@author – задает сведения об авторе;
@version – задает номер версии класса;
@exception – задает имя класса исключения;
@param – описывает параметры, передаваемые методу;
@return – описывает тип, возвращаемый методом;
@deprecated – указывает, что метод устаревший и у него есть более совершенный аналог;
@since – с какой версии метод (член класса) присутствует;
@throws – описывает исключение, генерируемое методом;
@see – что следует посмотреть дополнительно.
Из java-файла, содержащего такие комментарии, соответствующая утилита javadoc.exe может извлекать информацию для документирования классов
и сохранения ее в виде HTML-документа.
В качестве примера можно рассмотреть снабженный комментариями слегка измененный класс User из предыдущей главы.
package chapt02;
public class User {
/**
* personal user's code
*/
private int numericCode;
/**
* user's password
*/
private String password;
/**
* see also chapter #3 "Classes"
*/
public User() {
password = "default";
}
/**
* @return the numericCode
* return the numericCode
*/
public int getNumericCode() {
return numericCode;
}
/**
* @param numericCode the numericCode to set
* parameter numericCode to set
*/
public void setNumericCode(int numericCode) {
this.numericCode = numericCode;
}
/**
* @return the password
* return the password
*/
public String getPassword() {
return password;
}
/**
* @param password the password to set
* parameter password to set
*/
public void setPassword(String password) {
this.password = password;
}
}
Сгенерированный для этого класса HTML-документ будет иметь вид:
Рис. 2.1. Фрагмент документации для класса User
Операторы
Операторы Java практически совпадают с операторами C++ и имеют такой же приоритет. Поскольку указатели в Java отсутствуют, то отсутствуют операторы *, &, ->, delete для работы с ними. Операторы работают с базовыми типами и объектами классов-оболочек над базовыми типами. Операторы + и += производят также действия с по конкатенации с операндами типа String. Логические операторы ==, != и оператор присваивания = применимы к операндам любого объектного и базового типов, а также литералам. Применение оператора присваивания
к объектным типам часто приводит к ошибке несовместимости типов, поэтому такие операции необходимо тщательно контролировать.
Операции над числами: +,–,*, %,/,++,–– а также битовые операции &, |,^, ~ и операции сдвига аналогичны операциям C++. Деление на ноль целочисленного типа вызывает исключительную ситуацию, переполнение не контролируется.
Операции над числами с плавающей запятой практически те же, что и в других алгоритмических языках, но по стандарту IEEE 754 введены понятие бесконечности +Infinity и –Infinity и значение NaN (Not a Number), которое может быть получено, например, при извлечении квадратного корня из отрицательного числа.
Арифметические операторы