Тип int

Содержание:

Интерфейс

Интерфейс в Java относится к абстрактным типам данных. Они позволяют манипулировать коллекциями Java независимо от деталей их представления.

Кроме того, они образуют иерархию в объектно-ориентированных языках программирования.

//Generic interface definition
interface GenericInterface<T1, T2>
{
T2 PerformExecution(T1 x);
T1 ReverseExecution(T2 x);
}

//A class implementing generic interface
class Genericclass implements GenericInterface<String, Integer>
{
public Integer PerformExecution(String x)
{
//execution code
}
public String ReverseExecution(Integer x)
{
//execution code
}
}

Символы (тип char)

Для хранения символов Java использует специальный тип char. Он отличается от типа char в языках C/C++, где представляет собой целочисленный тип с размером 8 бит. В Java для char используется кодировка Unicode и для хранения Unicode-символов используется 16 бит или 2 байта. Диапазон допустимых значений — от 0 до 65536 (отрицательных значений не существует).

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

Не следует путать символ ‘a’ со строкой «a», состоящей из одного символа. На экране монитора они выглядят одинаково, но в программах ведут себя по разному.

Стандартные символы ASCII можно выводить сразу. Если нужно вывести специальный символ из Unicode, то можно воспользоваться шестнадцатеричным представлением кода в escape-последовательности — вы указываете обратную наклонную черту и четыре цифры после u. Например:

Хотя тип char используется для хранения Unicode-символов, его можно использовать как целочисленный тип, используя сложение или вычитание.

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

Если вы думаете, что увеличив значение переменной ch1 ещё на одну единицу, получите символ «й», то глубоко заблуждаетесь.

Чтобы узнать, какой символ содержится в значении переменной, заданной как int, можно воспользоваться двумя специальными методами из класса EncodingUtils:

Для стандартных символов ASCII:

Для расширенной таблицы символов:

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

В упрощённом виде, если работаем со стандартными символами ASCII (on 0 до 127), то можно получить символ из int ещё проще.

Класс Character

Класс Character является оболочкой вокруг типа char. Чтобы получить значение типа char, содержащее в объекте класса Character, вызовите метод charValue().

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

JSON

Сериализация и Десериализация

JSON — невероятно удобный и полезный синтаксис для хранения и обмена данными. Java полностью поддерживает это.

Прим. перев. Для использования JSON из примера необходимо подключить библиотеку JSON Simple.

Вы можете сериализовать данные следующим образом:

Получается следующая строка JSON:

Десериализация в Java выглядит так:

Используемый в примере файл JSON (jsonDemoFile.json):

Прим. перев. В Java проектах очень часто для работы с JSON используют библиотеки Gson от Google или Jackson. Обе библиотеки очень популярны и хорошо поддерживаются. Попробуйте и их.

Где нужны значения типа Boolean

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

Операторы ветвления

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

Есть два оператора ветвления (условные конструкции): if-else и switch.

Оператор if

Его синтаксис выглядит так:

if (условие) {

//код

} else {

//код

}

В круглых скобках после if указывают условное выражение (высказывание). Если оно истинно (то есть результат его вычисления равен true), то выполняется код в фигурных скобках после условия, иначе выполняется код по ветке else (если она есть).

Примеры:

Проверяем условие в круглых скобках:

3 > 2?

Если да, то в консоль выводим: «Высказывание в скобках правдивое», иначе ничего не выводим.

Так как 3 и правда больше 2, то в консоли появилось наше сообщение.

Печатаем «Высказывание в скобках правдивое», если результат вычислений в круглых скобках = true. В ином случае выводим: «Высказывание в скобках ложное».

Так как выражение (3 < 2) возвращает false, то и в консоли видим: «Высказывание в скобках ложное».

Вложенные конструкции

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

1. Вложенный if

Синтаксис тут такой:

if (условие1) {

//действия, если условие1 выполнено;

} else {

if (условие2) {

//действия, если условие2 выполнено;

}

<…>

else {

//действия, если все предыдущие условия не выполнились;

}

Пример:

2. Конструкция else if

Каждое логическое условие записывают через комбинацию else if, после которых в круглых скобках идёт альтернативное условие. Последний вариант (если ни одно из условий не сработало) записывается через else без условия.

Синтаксически это выглядит так:

if (условие1) {

//действия, если условие1 выполнено;

}

else if (условие2) {

//действия, если условие2 выполнено;

}

else if (условие3) {

//действия, если условие3 выполнено;

}

<…>

else {

//действия, если условие последнего if тоже не выполнилось;

}

Пример:

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

Иногда условий else if нужно довольно много:

Такой код сложно воспринимать: увеличение глубины вложенности быстро сделало его запутанным. Для таких случаев придуман оператор switch.

Методы

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

public static <T> int countAllOccurrences(T[] list, T element) {
int count = 0;
if (element == null) {
for ( T listElement : list )
if (listElement == null)
count++;
}
else {
for ( T listElement : list )
if (element.equals(listElement))
count++;
}
return count;
}

Если вы передадите список String для поиска в этом методе, он будет работать нормально. Но если вы попытаетесь найти число в списке строк, это даст ошибку времени компиляции.

2.1 Целочисленные типы (Integral types)

Integer-типы и тип char все вместе называются integral-типами.

2.1.1 Символьный тип char

Тип char является беззнаковым. Его значения это беззнаковые целые, которые обозначают все 65536 (216) символов из 16-битной Unicode таблицы символов. Эта таблица включает буквы, цифры и специальные символы.

Диапазон символьных значений

Первые 128 символов Unicode-таблицы такие же как и в 7-битной ASCII таблице символов. А первые 256 символов Unicode-таблицы такие же как и в 8-битной ISO Latin-1 таблице символов.
Символьный литерал заключается в одинарные кавычки (‘). Все символьные литералы имеют примитивный тип данных char. Символьный литерал представляется в соответствии с 16-битной символьной таблицей Unicode, которая включает в себя символы 8-битной ISO-Latin-1 и 7-битной ASCII

Обратите внимание, что цифры (0-9), прописные буквы (A-Z) и строчные буквы (a-z) имеют Unicode значения расположенные соответственно их порядку

Примеры символьных литералов

Unicode символ может быть указан как четырехзначное шестнадцатеричное число (т.е. 16-бит) с префиксом \u.Escape-последовательности (escape sequences) определяются специальные символы. Для определения символьного литерала они должны быть заключены в одинарные кавычки. Например, символьные литералы \t и \u0009 являются эквивалентными. Однако символьные литералы \u000a и \u000d не должны использоваться в исходном коде для представления новой строки и возврата каретки. Компилятор интерпретирует эти значения как терминаторы строк, что вызовет ошибку компиляции. Вам следует использовать escape-последовательности \n и \r для правильной интерпретации этих символов в коде.

Вы также можете использовать escape-последовательность \ddd чтобы указывать символьный литерал как восьмеричное значение, где вместо каждой d может быть восьмеричная цифра (0–7). Количество цифр должно быть три или меньше и восьмеричное значение не должно превышать \377; другими словами только первые 256 символов могут быть указаны в такой нотации.

Примеры \ddd escape-последовательностей

2.1.2 Целые типы (integer types): byte, short, int, long

Целые типы данных (Integer data types) охватывают следующие типы данных:  int, long, byte и short. Типы byte, short, int, long являются знаковыми.
Примитивные значения данных не являются объектами. Каждый примитивный тип данных определяет диапазон значений и операции над этими значениями с помощью операторов. Каждый примитивный тип данных имеет соответствующий ему класс-обертку (wrapper), который может быть использован для представления значения в форме объекта.

Диапазон целых значений

По умолчанию для любого целочисленного литерала используется тип int. Но если добавить к целочисленному значению суффикс L (или l), то будет использоваться тип long. Обычно используют суффикс L потому что l похож на единицу, что может вызвать путаницу. Без суффикса L long-литералы 2000L и 0L будут интерпретированы как int-литералы. Не существует прямого способа чтобы указать short и byte литерал.

В дополнение целочисленные литералы могут быть указаны не только в десятичной системе счисления, но в двоичной (основание 2, цифры 0-1), восьмеричной (основание 8, цифры 0-7) и шестнадцатеричной (основание 16, цифры 0-9 и a-f). Цифры от a до f в шестнадцатеричной системе счисления соответствуют числам от 10 до 15. Двоичные, восьмеричные и шестнадцатеричные числа указываются с префиксами оснований 0b (или 0B), 0, и 0x (или 0X) соответственно. Шестнадцатеричные цифры от a до f также могут указываться в верхнем регистре (A-F). Негативные целые (например, -90) указываются с минусом (-) в виде префикса независимо от системы счисления (например, -0b1011010, -0132, или -0X5A).

Примеры десятичных, бинарных, восьмеричных и шестнадцатеричных литералов

Символьные и строковые литералы

Значения символьного типа могут быть выражены с помощью символов Unicode (UTF-16), заключенных в одинарные кавычки, которые в конечном итоге могут быть выражены в форме кодировки с использованием «escape-последовательности Unicode»:

char simbol = 'ñ';
char code = '\u00F1';

Также поддерживаются некоторые специальные представления (известные как escape-последовательности):

Побег символ
\b backspace (назад)
\t табуляция
\n перевод строки (конец строки)
\f перевод страницы (конец страницы / новая страница)
\r возврат каретки
\’ отображение одинарной кавычки
\” отображение двойной кавычки
\\ обратная косая черта ( \)

Типы с плавающей точкой

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

Слово «плавающая» означает, что десятичная точка может располагаться в любом месте (она «плавает»). Вот коты плавать не особенно любят, поэтому они не float и не double.

Тип float

Тип float определяет значение одинарной точности, которое занимает 32 бит. Переменные данного типа удобны, когда требуется дробная часть без особой точности, например, для денежных сумм.

Рекомендуется добавлять символ F или f для обозначения этого типа, иначе число будет считаться типом double.

Конвертируем из строки.

Класс Float является оболочкой для данного типа. Без необходимости не используйте в Android класс Float.

Также есть специальный класс BigDecimal для проведения арифметических действий повышенной точности (финансовые расчёты).

Тип double

Тип double обеспечивает двойную точность, что видно из его названия (double — двойная). Занимает 64 бит для хранения значений. Многие математические функции возвращают значения типа double. Кстати, современные процессоры оптимизированы под вычисления значений двойной точности, поэтому они предпочтительнее, чем тип float.

Тип double содержит не только числа, но и слова. Сейчас вам докажу. Разделим число типа double на ноль. Ошибки не произойдёт.

Пример вернёт значение Infinity (Бесконечность). Если разделить отрицательное число на ноль, то вернётся -Infinity.

А что произойдёт, если сложить две бесконечности? Если рассуждать логически, то сломается интернет, наступит конец света или можно вызвать Волдеморта. Я долго не решался, но потом набрался храбрости и попробовал.

Вернулось ещё одно слово — NaN. Что это вообще? Может должно вернуться Nyan — ну вы знаете, это странный котик, который летит бесконечно в космосе, оставляя за собой шлейф из радуги.

Умножать две бесконечности я побоялся. И вам не советую.

Класс Double является оболочкой для данного типа. Без необходимости не используйте в Android класс Double.

Конвертация double в строку

При работе с числами double следует держать ухо востро. Рассмотрим пример конвертации трёх чисел.

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

Первый способ — используем String.format().

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

Второй способ — метод Double.toString(). У меня метод превратил число в «непонятную» строку. А у некоторых этот пример возвращал строку в нормальном виде. Не заслуживает доверия.

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

Четвёртый экзотический способ, которым редко пользуются — DecimalFormat.

Производительность и лучшие практики

Пустая коллекция вместо Null

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

Создание объектов только когда необходимо

Создание объектов — одна из самых затратных операций в Java. Лучше всего создавать их только тогда, когда они действительно нужны.

Deadlocks (Дедлоки)

Взаимная блокировка (Deadlock) потоков может происходить по многим причинам, и полностью защититься от них в Java 8 очень сложно. Чаще всего, это происходит, когда один синхронизируемый объект ожидает ресурсов, которые заблокированы другим синхронизированным объектом.

Вот пример тупика этого потока:

Результат этой программы:

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

Вывод:

Autoboxing и unboxing

Однако даже простые операции с типом писать непросто.

Как мы уже говорили, тип — неизменяемый (immutable). Чтобы создать объект типа с новым значением типа , нужно явно создать новый объект . Зато получить значение типа , которое хранится внутри объекта , просто: нужно вызвать метод .

Пример:

Код Описание
Оборачиваем в класс
Получаем значение из объекта
Создаем новое значение

Код довольно громоздкий, не находите?

Создатели Java тоже так считают, поэтому они научили компилятор делать эти операции автоматически. Автоматическое преобразование в называется autoboxing (box — коробка, класть в коробку), а обратная операция — к — unboxing.

Ваш код Что видит компилятор

Благодаря autoboxing и unboxing, вы можете спокойно присваивать тип типу и наоборот. Можно писать выражения любой сложности и не делать различий между типами и .

Примеры:

Код Что сгенерирует компилятор

7. Пример описания и использования переменных с плавающей запятой.
float f;    // переменная с именем f типа float
f = 3.998f; // переменной f присвоить значение 3.998

double d; // переменная с именем d типа double
d = -340.349489287;

float ff = -3.99f; // инициализация переменной ff типа float

double dd = 779303028.3398; // инициализация переменной dd типа double
8. Какие особенности применения символьного типа данных char?

В Java для представления символов введен тип данных char.

Переменные типа char занимают в памяти компьютера 16 бит. Это связано с тем, что для представления символов char в Java используется кодировка Юникод (Unicode). Диапазон допустимых значений этого типа составляет от до 65535.

Использование кодировки Unicode связано с тем, что программы на Java используются во всем мире, то есть в странах, где для представления символа нужно 2 байта (16 бит). Такими странами есть, например, Япония, Китай.

9. Примеры описания и использования переменных типа char
char c;  // переменная с именем c типа char
c = 'Z'; // присвоение литерала 'Z'
c = 90;  // c = 'Z'
c--;     // c = 'Y'

char cc = '2'; // инициализация переменной cc значением '2'
10. Какое назначение в программах имеет тип boolean?

Тип boolean предназначен для сохранения логических значений. Переменные типа boolean могут принимать только два значения: true или false.

Пример использования переменных типа boolean.

boolean b;
b = true;
boolean bb=false; // bb = false

bb = 5>4;   // bb = true
bb = 55==6; // bb = false
11. Какие отличия между примитивными типами и типами-ссылками?

Между примитивными типами и типами-ссылками существуют следующие отличия:

  • переменная примитивного типа не является переменной-ссылкой (объектом);
  • переменная примитивного типа есть «автоматической» и сохраняется в стеке. Объект (переменная) сохраняется в «куче». Поэтому производительность работы с переменными примитивного типа выше;
  • при объявлении переменной примитивного типа не нужно выделять память оператором new;
  • переменная примитивного типа напрямую сохраняет свое значение в отличие от переменной-ссылки.
12. Существуют ли беззнаковые примитивные типы?

Нет. В языке Java все примитивные типы являются знаковыми, то есть, могут принимать отрицательные значения.

  • Литералы. Идентификаторы. Ключевые слова. Комментарии
  • Переменные. Объявление переменных. Инициализация переменных
  • Преобразование и приведение типов. Автоматическое продвижение типов в выражениях

Конструкторы

  • Их единственная цель — создавать экземпляры класса. Они вызываются в процессе создания объекта класса.
  • Если конструктор с аргументами определен в классе, то нельзя будет работать со стандартным конструктором без аргументов (no-argument constructor) — придется их прописать.
  • Java не поддерживает конструктор копирования.
  • Имя конструктора и класса совпадает.
  • Если конструктор вызывается из другого конструктора с синтаксисом this, то речь идет именно об этом объекте.
  • В Java есть стандартный конструктор.

Приватный конструктор:

  • Защищает класс от явного превращения в экземпляр.
  • Построение объекта возможно только внутри конструктора.
  • Используется в шаблоне «Одиночка» (Singleton).

Вопрос: Можно ли синхронизировать конструкторы в Java?

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

Вопрос: Наследуются ли конструкторы? Может ли подкласс вызывать конструктор родительского класса?

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

Булевы значения

Тип boolean предназначен для хранения логических значений и может принимать только одно из двух возможных значений: true или false. Данный тип всегда возвращается при использовании операторов сравнения (больше, меньше, равно, больше или равно, меньше или равно, не равно). Также он используется в управляющих операторах if и for.

В отличие от реальной жизни, где вполне может состояться диалог:

В компьютерной программе нужно чётко определиться — истина или ложь.

В операторах if используется укороченная запись при значении true:

Java сам поймёт, что переменную check нужно сравнить с true.

Класс Boolean

Класс Boolean является оболочкой вокруг значений типа boolean. Чтобы получить значение типа boolean из объекта класса Boolean, используйте метод booleanValue(). Тип boolean использует 4 байт памяти, а Boolean — 16. Вывод понятен?

Ещё один совет, применимый ко всем типам. Допустим, нам нужно объявить 32 переменных типа boolean:

Умножаем 4 байта на 32 переменных и получаем 128 байт занятой памяти. А если объявим массив:

Считаем: 4 + 8 + 8 + 32 * 1 = 52. С учётом выравнивания памяти по 8 байт, получаем не 52, а 56. Всё равно меньше, чем в первом примере.

Конвертируем строку в булево значение.

Конвертируем булево значение в строку.

Приведение типов

Когда мы производим какие-то действия с переменными, то нужно следить за типами. Нельзя умножать котов на футбольные мячи, это противоречит здравому смыслу. Также и с переменными. Если вы присваиваете переменной одного типа значение другого типа, то вспоминайте теорию. Например, вы без проблем можете присвоить значение типа int переменной типа long, так как все числа из диапазона типа int гарантировано помещаются в диапазон чисел long. В этом случае Java выполнит преобразование автоматически, вы даже ничего не заметите.

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

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

Таблица выглядит следующим образом.

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

Типы целых чисел и чисел с плавающей точкой совместимы частично. Например, число 5 вполне может быть числом с плавающей точкой (5.0).

Совсем не совместимы, например, char и boolean.

С автоматическим приведением мы разобрались. Рассмотрим вариант, когда нужно преобразовать число типа int в число типа byte. Преобразование автоматически невозможно, поскольку byte меньше int. Но, например, число 99 вполне можно использовать и как int и как byte. В этом случае используется явное приведение типов, то есть преобразование из одного типа в другой (преобразование с сужением).

Выглядит это следующим образом:

Как видите, вы в скобках указываете тип, к которому нужно явно привести переменную.

Существует ещё вариант приведения с усечением. Это когда число с плавающей точкой приводится к целочисленному типу. В этом случае отбрасывается дробная часть (хвост). Например, число 3.14 будет усечено до числа 3:

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

Например, попробуйте преобразовать число 454.874 в тип byte:

У меня вывелся удивительный результат: b равно -58.

Рассмотрим такой пример. Допустим у нас есть выражение, где промежуточное значение может выходить за пределы допустимого диапазона:

При умножении переменных a * b промежуточный результат вышел за пределы диапазона допустимых значений для типов byte. Java во время вычисления промежуточных результатов автоматически повышает тип каждого операнда до int и ошибки не происходит.

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

С виду всё правильно. Если не слишком больше число типа byte, а итоговый результат тоже не выходит за диапазон допустимых значений. Но Java не позволит вам написать подобный код. Происходит следующее. Во время вычисления выражения тип операндов был автоматически повышен до int, как об этом говорилось выше. При этом тип результата тоже был повышен до int. Получается, что результат вычисления равен типу int, а мы пытаемся его присвоить переменной b, которая у нас объявлена как byte. И это несмотря на то, что итоговый результат может быть типом byte. Как же выйти из этого положения? Следует использовать явное приведение типов:

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

Типы всех значений byte, short, char повышаются до типа int, как это было рассмотрено выше.

Если один операнд имеет тип long, то тип всего выражения повышается до long.

Если один операнд имеет тип float, то тип всего выражения повышается до float.

Если один операнд имеет тип double, то тип всего выражения повышается до double.

В первом промежуточном выражении (f * b) тип переменной b повышается до float и промежуточный результат также становится float. В следующем выражении (i / c) тип у переменной c повышается до int и промежуточный результат также становится типом int. В выражении (d * s) тип переменной s повышается до double и промежуточное выражение также становится double. В результате у нас появились три промежуточные значения типов: float, int, double. При сложении float и int мы получаем float, затем при вычитании с использованием float и double тип повышается до double, который и становится окончательным типом результата выражения.

Абстрактный класс и интерфейс

  • В интерфейсе отсутствует код реализации, а все методы являются абстрактными. То есть, все методы объявляются, но ни один не определяется.
  • В абстрактном классе есть исполняемые и абстрактные методы.
  • Класс реализует сколько угодно интерфейсов, но расширяет только один абстрактный класс.
  • Методы абстрактного класса могут быть или не быть абстрактными.
  • Абстрактный класс не может превратиться в экземпляр, но может стать подклассом.
  • Все абстрактные методы должны определяться в подклассе, то есть, подкласс является абстрактным.
  • Создавать экземпляры из интерфейса нельзя. Их можно реализовывать в других классах или расширять другими интерфейсами.
  • Переменные интерфейсов конечные и статические. По умолчанию, все методы интерфейса публичные и абстрактные.
  • Интерфейс не может содержать реализацию и не может превращаться в подкласс. Все переменные должны быть постоянными.
Добавить комментарий

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

Adblock
detector