Java для начинающих. часть 1 из 4

Нам доверяют

Static

  • Модификатор Static используется для создания чего-то в единственном экземпляре. Например, когда мы хотим создать переменную или объект, доступные для всех объектов класса.
  • Static необходим для передачи информации по всем объектам.
  • Static подходит для переменных, методов и блоков.
  •  или переменные принадлежат классу, а не объекту.
  • Статичный метод или переменная инициализируются один раз перед переменной экземпляра.
  • Статичный метод или переменная могут вызываться напрямую из имени класса. Пример: <className>.<variableName>
  • Статичный метод имеет доступ только к статичным данным.
  • Статичный метод не может ссылаться на this или super.
  • Статичный метод может вызывать только другие статичные методы.
  • main () — это статичный метод. Он должен быть доступен приложению до создания экземпляров.
  • Конструктор не бывает статичным, потому как компилятор считает его методом. Кроме того, конструктор нужен для инициализации нового объекта, а static выполняет совершенно противоположную функцию.
  • Статичная переменная загружается первой. После нее идет статичный блок. И очередность здесь важна. Статичные методы загружаются в конце.

· Иерархия следующая:

Присваивание

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

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

Тот факт, что является оператором, а не «магической» конструкцией языка, имеет интересные последствия.

Большинство операторов в JavaScript возвращают значение. Для некоторых это очевидно, например сложение или умножение . Но и оператор присваивания не является исключением.

Вызов записывает в и возвращает его.

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

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

Забавное применение присваивания, не так ли? Нам нужно понимать, как это работает, потому что иногда это можно увидеть в JavaScript-библиотеках.

Однако писать самим в таком стиле не рекомендуется. Такие трюки не сделают ваш код более понятным или читабельным.

Рассмотрим ещё одну интересную возможность: цепочку присваиваний.

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

Опять-таки, чтобы код читался легче, лучше разделять подобные конструкции на несколько строчек:

Польза от такого стиля особенно ощущается при быстром просмотре кода.

Java Integer Math

Математические операции, выполняемые с целочисленными типами Java (byte, short, int и long), ведут себя немного иначе, чем обычные математические операции. Поскольку целочисленные типы не могут содержать дроби, в каждом вычислении с одним или несколькими целочисленными типами все дроби в результате обрезаются. Посмотрите на это математическое выражение:

int result = 100 / 8;

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

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

С плавающей точкой Math

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

double result = 100 / 8;

Несмотря на то, что переменная результата теперь имеет тип с плавающей запятой (double), конечный результат по-прежнему равен 12 вместо 12,5. Причина в том, что оба значения в математическом выражении (100 и 8) оба являются целыми числами. Таким образом, результат деления одного на другое сначала преобразуется в целое число (12), а затем присваивается переменной результата.

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

double no1 = 100;
double no2 = 8;

double result = no1 / no2;

Теперь переменная результата будет иметь значение 12,5.

В Java есть способ заставить все числа в расчете быть переменными с плавающей точкой. Вы ставите числа с большой буквы F или D. Вот пример:

double result = 100D / 8D;

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

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

Точность с плавающей точкой

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

Посмотрите:

double resultDbl3 = 0D;
System.out.println("resultDbl3 = " + resultDbl3);

for(int i=0; i<100; i++){
    resultDbl3 += 0.01D;
}
System.out.println("resultDbl3 = " + resultDbl3);

Вывод выводится при выполнении этого кода с Java 8:

resultDbl3 = 0.0
resultDbl3 = 1.0000000000000007

Первый оператор System.out.println() правильно печатает значение 0.0, которое является начальным значением переменной resultDbl3.

Однако второй оператор System.out.println() выводит несколько странный результат. Добавление значения 0,01 к 0 всего 100 раз должно привести к значению 1,0, верно? Но каким-то образом окончательный результат 1.0000000000000007. Как видите, что-то не так во фракциях.

Обычно неточность с плавающей запятой незначительна, но все же важно знать об этом

2 Целые типы

В языке Java аж 4 целых типа: , , и . Они отличаются размером и диапазоном значений, которые могут хранить.

Тип

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

Переменные этого типа могут принимать значение от до .

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

Примеры:

Код Пояснение
Метод возвращает длину строки
Поле содержит длину массива.

Тип

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

То есть в нем даже число миллион не сохранишь. Даже 50 тысяч. Это самый редко используемый целочисленный тип в Java. В основном его используют, когда хотят сэкономить на памяти.

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

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

Тип

Этот тип получил свое название от — его еще называют длинное целое. В отличие от типа , у него просто гигантский диапазон значений: от до

Почему же он не является основным целым типом?

Все дело в том, что Java появилась еще в середине 90-х, когда большинство компьютеров были 32-х разрядными. А это значило, что все процессоры были заточены под работу с числами из 32-х бит. С целыми числами из 64-х бит процессоры работать уже умели, но операции с ними были медленнее.

Поэтому программисты разумно решили сделать стандартным целым типом тип , ну а тип использовать только тогда, когда без него действительно не обойтись.

Тип

Это самый маленький целочисленный тип в Java, но далеко не самый редко используемый. Его название совпадает со словом — минимальная адресуемая ячейка памяти в Java.

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

Например, вам нужно куда-то скопировать файл.

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

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

Арифметические операции

Арифметические операции позволяют выполнять сложение (операция «+»), вычитание (операция «-»), умножение (операция «*»), деление (операция «/») и взятие остатка (операция «%»).  Эти операции имеют такие же приоритеты, что и в обычной математике, которую изучают в школе, то есть умножение и деление выполняется перед сложением и вычитанием.

Java

double x1 = 1.1 + 2.3; // 3.4
double x2 = 1.1 — 0.1; // 1.0
double x3 = 1.1 * 2 + 1; // 3.2
double x4 = 6 / 2.0; // 3.0
int x5 = 12 + 3; // 15
int x6 = 13 % 5; // 3

1
2
3
4
5
6

doublex1=1.1+2.3;// 3.4

doublex2=1.1-0.1;// 1.0

doublex3=1.1*2+1;// 3.2

doublex4=62.0;// 3.0

intx5=12+3;// 15

intx6=13%5;// 3

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

При выполнении арифметической операции над операндами разных типов результат операции будет иметь наибольший тип, Что можно описать следующими правилами:

  1. Если один из операндов имеет тип
    double, то результат выражения имеет тип
    double, иначе смотри пункт 2.
  2. Если один из операндов имеет тип
    float, то результат выражения имеет тип
    float, иначе смотри пункт 3.
  3. Если один из операндов имеет тип
    long, то результат выражения имеет тип
    long, иначе результат выражения имеет тип
    int.

(например, при сложении
int  и
long  результат будет иметь тип
long, а при сложении
long  и
float  результат будет иметь тип
float, а при сложении
float  и
double  результат будет иметь тип
double).

Если результат операции с целочисленными данными выходит за диапазон, то старшие биты отбрасываются, и результирующее значение будет совершенно неверным. При попытке деления на 0 возникает исключение
java.lang.ArithmeticExceptionzero.

При выполнении операций с плавающей точкой при выходе за верхнюю или нижнюю границу диапазона получается
+Infinity  (
Double.POSITIVE_INFINITY  и
Float.POSITIVE_INFINITY) и
-Infinity  (
Double.NEGATIVE_INFINITY  и
Float.NEGATIVE_INFINITY ) соответственно, а при получении слишком маленького числа, которое не может быть нормально сохранено в этом типе данных получается -0.0 или +0.0.

При выполнении операций с плавающей точкой результат
NaN  (
Double.NaN  и
Float.NaN) получается в следующих случаях:

  • Когда один из операндов
  • В неопределённых результатах:
    • Деления 0/0, ∞/∞, ∞/−∞, −∞/∞,  −∞/−∞
    • Умножения 0×∞ and 0×−∞
    • Степень 1∞
    • сложения ∞ + (−∞), (−∞) + ∞ и эквивалентные вычитания.
  • Операции с комплексными результатами:
    • Квадратный корень из отрицательного числа
    • Логарифм отрицательного числа
    • Тангенс 90 градусов и ему подобных (или π/2 радиан)
    • Обратный синус и косинус от числа меньше −1 и больше +1.

4 Деление целых и вещественных чисел в Java

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

Поначалу кажется, что правильный вариант такой:

Однако не все так просто. Дело в том, что Java-машина сначала вычислит значение выражения и только потом присвоит результат в переменную . А деление выполнится нацело. Т.е.  будет содержать или, если быть более точным,

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

В любом из этих выражений  будет содержать значение

А как же быть с переменными? Что если у нас есть такой код:

Тут есть хитрое (и очевидное) решение — заставить Java-машину преобразовать переменные в вещественные, умножив их на вещественную единицу — 

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

Примеры:

Команда Порядок выполнения Результат

Платформы

Платформа может относиться к типу процессора (ЦП) или другому оборудованию, на котором работает данная операционная система или приложение , типу операционной системы на компьютере или комбинации типа оборудования и типа операционной системы, работающей на нем. Пример распространенной платформы — Microsoft Windows, работающая на архитектуре x86 . Другие хорошо известные платформы настольных компьютеров включают Linux / Unix и macOS, обе из которых сами по себе являются кроссплатформенными. Однако существует множество устройств, таких как смартфоны , которые также фактически являются компьютерными платформами, но о них меньше думают. Прикладное программное обеспечение может быть написано в зависимости от функций конкретной платформы — оборудования, операционной системы или виртуальной машины, на которой оно работает. Платформа Java — это платформа виртуальных машин, которая работает во многих операционных системах и типах оборудования, и является общей платформой для написания программного обеспечения.

Аппаратные платформы

Аппаратная платформа может относиться к архитектуре набора команд . Например: архитектура x86 и ее варианты, такие как IA-32 и x86-64 . На этих машинах часто работает одна версия Microsoft Windows, хотя они могут работать и с другими операционными системами, включая Linux, OpenBSD , NetBSD , macOS и FreeBSD .

Эти 32-разрядные архитектуры ARM (и более новые 64-разрядные версии) является общим на смартфоны и планшетные компьютеры , которые работают Android , IOS , и других мобильных операционных систем .

Программные платформы

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

  • BlackBerry
  • Android для смартфонов и планшетных компьютеров (x86, ARM)
  • iOS ( ARM )
  • Microsoft Windows (x86, ARM

    Интерфейс командной строки , также известный под именами реализации .NET Framework (от Microsoft) и кроссплатформенным вариантом Mono (ранее Novell, а теперь Xamarin )

    )

  • Ява
  • Веб-браузеры — более или менее совместимы друг с другом, работают с веб-приложениями на JavaScript.
  • Linux (x86, PowerPC, ARM и другие архитектуры)
  • macOS (x86, PowerPC (на 10.5 и ниже))
  • Mendix
  • Solaris (SPARC, x86)
  • SymbianOS
  • SPARC
  • PlayStation 4 (x86), PlayStation 3 (на базе PowerPC) и PlayStation Vita (ARM)
  • Unix
Незначительный / исторический
  • AmigaOS (m68k), AmigaOS 4 (PowerPC), AROS (x86, PowerPC, m68k), MorphOS (PowerPC)
  • Atari TOS , MiNT
  • BSD (многие платформы; см., Например, NetBSDnet)
  • Системы типа DOS на x86: MS-DOS , IBM PC DOS , DR-DOS , FreeDOS
  • OS / 2 , eComStation

Платформа Java

Как отмечалось ранее, платформа Java является исключением из общего правила, согласно которому операционная система является программной платформой. Язык Java обычно компилируется в виртуальную машину: виртуальный ЦП, на котором выполняется весь код, написанный для языка. Это позволяет запускать один и тот же исполняемый двоичный файл во всех системах, реализующих виртуальную машину Java (JVM). Программы Java могут выполняться изначально с использованием процессора Java . Это нечасто и в основном используется для встроенных систем.

Код Java, работающий в JVM, имеет доступ к службам, связанным с ОС, таким как дисковый ввод-вывод и доступ к сети, если предоставлены соответствующие привилегии. JVM делает системные вызовы от имени приложения Java. Эта настройка позволяет пользователям выбирать соответствующий уровень защиты в зависимости от ACL . Например, дисковый и сетевой доступ обычно разрешен для настольных приложений, но не для апплетов на основе браузера . JNI также можно использовать для обеспечения доступа к специфическим функциям операционной системы.

В настоящее время программы Java Standard Edition могут работать в Microsoft Windows, macOS, нескольких Unix-подобных операционных системах и еще нескольких не-UNIX-подобных операционных системах, таких как встроенные системы. Для мобильных приложений используются плагины браузера для устройств на базе Windows и Mac, а Android имеет встроенную поддержку Java. Также существуют подмножества Java, такие как Java Card или Java Platform, Micro Edition , предназначенные для устройств с ограниченными ресурсами.

Побитовые операции

  • Побитовые операции рассматривают исходные числовые значения как поля битов и выполняют над ними следующие действия:
  • установка бита в i
    -ой позиции поля результата в 1 , если оба бита в i
    -ых позициях операндов равны 1 , или в 0 в противном случае – побитовое И (» & «);
  • установка бита в i
    -ой позиции поля результата в 1 , если хотя бы один бит в i
    -ых позициях операндов равен 1 , или в 0 в противном случае – побитовое ИЛИ (» | «);
  • установка бита в i
    -ой позиции поля результата в 1 , если биты в i
    -ых позициях операндов не равны друг другу, или в 0 в противном случае – побитовое исключающее ИЛИ (» ^ «);
  • сдвиг влево битов поля первого операнда на количество битов, определяемое вторым операндом (бит знака числа при этом не меняется) – побитовый сдвиг влево с учетом знака »
  • сдвиг вправо битов поля первого операнда на количество битов, определяемое вторым операндом (бит знака числа при этом не меняется) – побитовый сдвиг вправо с учетом знака » >> «;
  • сдвиг вправо битов поля первого операнда на количество битов, определяемое вторым операндом (бит знака числа при этом также сдвигается) – побитовый сдвиг вправо без учета знака » >>> «.

Примеры побитовых операций:

  1. Побитовое И

    int
    x =
    112
    ;

    int
    y =
    94
    ;

    int
    z;

    z =
    x &
    y;

    // z=80: 00000000 00000000 00000000 01010000

  2. Побитовое ИЛИ

    int
    x =
    112
    ;

    // x: 00000000 00000000 00000000 01110000

    int
    y =
    94
    ;

    // y: 00000000 00000000 00000000 01011110

    int
    z;

    z =
    x |
    y;

    // z = 126: 00000000 00000000 00000000 01111110

  3. Побитовое исключающее ИЛИ

    int
    x =
    112
    ;

    // x: 00000000 00000000 00000000 01110000

    int
    y =
    94
    ;

    // y: 00000000 00000000 00000000 01011110

    int
    z;

    z =
    x ^
    y;

    // z = 46: 00000000 00000000 00000000 00101110

  4. Сдвиг влево с учетом знака

    int
    x =
    31
    ,
    z;

    // x: 00000000 00000000 00000000 00011111

    z =
    x // z = 124: 00000000 00000000 00000000 01111100

  5. Сдвиг вправо с учетом знака

    int
    x =

    17
    ,
    z;

    z =
    x >>
    2
    ;

    // z = -5: 11111111 11111111 11111111 11111011

  6. Сдвиг вправо без учета знака

    int
    x =

    17
    ,
    z;

    // x: 11111111 11111111 11111111 11101111

    z =
    x >>>
    2
    ;

    // z = 1073741819

    // z: 00111111 11111111 11111111 11111011

Условные выражения

Последнее обновление: 16.04.2018

Условные выражения представляют собой некоторое условие и возвращают значение типа boolean, то есть значение true
(если условие истинно), или значение false (если условие ложно). К условным выражениям относятся операции сравнения и логические
операции.

Операции сравнения

В операциях сравнения сравниваются два операнда, и возвращается значение типа — , если выражение верно,
и , если выражение неверно.

  • сравнивает два операнда на равенство и возвращает (если операнды равны) и (если операнды не равны)

    int a = 10;
    int b = 4;
    boolean c = a == b;    		// false
    boolean d = a == 10;       // true
    
  • сравнивает два операнда и возвращает , если операнды НЕ равны, и , если операнды равны

    int a = 10;
    int b = 4;
    boolean c = a != b;    		// true
    boolean d = a != 10;       // false
    
  • < (меньше чем)

    Возвращает , если первый операнд меньше второго, иначе возвращает

    int a = 10;
    int b = 4;
    boolean c = a < b; 	// false
    
  • > (больше чем)

    Возвращает , если первый операнд больше второго, иначе возвращает

    int a = 10;
    int b = 4;
    boolean c = a > b; 	// true
    
  • >= (больше или равно)

    Возвращает , если первый операнд больше второго или равен второму, иначе возвращает

    boolean c = 10 >= 10; 	// true
    boolean b = 10 >= 4; 	// true
    boolean d = 10 >= 20; 	// false
    
  • <= (меньше или равно)

    Возвращает , если первый операнд меньше второго или равен второму, иначе возвращает

    boolean c = 10 <= 10; 	// true
    boolean b = 10 <= 4; 	// false
    boolean d = 10 <= 20; 	// true
    

Логические операции

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

  • (c равно , если либо a, либо b (либо и a, и b) равны , иначе c будет равно )

  • (c равно , если и a, и b равны , иначе c будет равно )

  • (c равно , если b равно , иначе c будет равно )

  • (c равно , если либо a, либо b (но не одновременно) равны , иначе c будет равно )

  • (c равно , если либо a, либо b (либо и a, и b) равны , иначе c будет равно )

  • (c равно , если и a, и b равны , иначе c будет равно )

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

Выражение будет вычислять сначала оба значения — a и b и на их основе выводить результат.

В выражении же
вначале будет вычисляться значение a, и если оно равно , то вычисление значения b уже смысла не имеет,
так как у нас в любом случае уже c будет равно . Значение b будет вычисляться только в том случае, если a равно

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

В выражении же сначала будет вычисляться значение a,
и если оно равно , то вычисление значения b уже не имеет смысла, так как значение c в любом случае равно .
Значение b будет вычисляться только в том случае, если a равно

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

Примеры:

boolean a1 = (5 > 6) || (4 < 6); // 5 > 6 - false, 4 < 6 - true, поэтому возвращается true
boolean a2 = (5 > 6) || (4 > 6); // 5 > 6 - false, 4 > 6 - false, поэтому возвращается false
boolean a3 = (5 > 6) && (4 < 6); // 5 > 6 - false, поэтому возвращается false (4 < 6 - true, но не вычисляется)
boolean a4 = (50 > 6) && (4 / 2 < 3); // 50 > 6 - true, 4/2 < 3 - true, поэтому возвращается true
boolean a5 = (5 > 6) ^ (4 < 6); // 5 > 6 - true, поэтому возвращается true (4 < 6 - false)
boolean a6 = (50 > 6) ^ (4 / 2 < 3); // 50 > 6 - true, 4/2 < 3 - true, поэтому возвращается false

НазадВперед

Операции с числами

Последнее обновление: 05.02.2018

Арифметические операции

Kotlin поддерживает базовые арифметические операции:

  • + (сложение): возвращает сумму двух чисел.

    val x = 5
    val y = 6
    val z = x + y
    println(z)      // z = 11
    
  • — (вычитание): возвращает разность двух чисел.

    val x = 5
    val y = 6
    val z = x - y  // z = -1
    
  • * (умножение): возвращает произведение двух чисел.

    val x = 5
    val y = 6
    val z = x * y  // z = 30
    
  • (деление): возвращает частное двух чисел.

    val x = 60
    val y = 10
    val z = x / y  // z = 6
    
  • %: возвращает остаток от целочисленного деления двух чисел.

    val x = 65
    val y = 10
    val z = x % y  // z = 5
    
  • ++ (инкремент): увеличивает значение на единицу.

    Префиксный инкремент возвращает увеличенное значение:

    var x = 5
    val y = ++x
    println(x)      // x = 6
    println(y)      // y = 6
    

    Постфиксный инкремент возвращает значение до увеличения на единицу:

    var x = 5
    val y = x++
    println(x)      // x = 6
    println(y)      // y = 5
    
  • — (декремент): уменьшает значение на единицу.

    Префиксный декремент возвращает уменьшенное значение:

    var x = 5
    val y = --x
    println(x)      // x = 4
    println(y)      // y = 4
    

    Постфиксный декремент возвращает значение до уменьшения на единицу:

    var x = 5
    val y = x--
    println(x)      // x = 4
    println(y)      // y = 5
    

Также есть ряд операций присвоения, которые сочетают арифметические операции и присвоение:

  • +=: присваивание после сложения. Присваивает левому операнду сумму левого и правого операндов: A += B эквивалентно
    A = A + B

  • -=: присваивание после вычитания. Присваивает левому операнду разность левого и правого операндов:
    A -= B эквивалентно A = A — B

  • *=: присваивание после умножения. Присваивает левому операнду произведение левого и правого операндов:
    A *= B эквивалентно A = A * B

  • /=: присваивание после деления. Присваивает левому операнду частное левого и правого операндов:
    A /= B эквивалентно A = A / B

  • %=: присваивание после деления по модулю. Присваивает левому операнду остаток от целочисленного деления левого операнда на правый:
    A %= B эквивалентно A = A % B

Побитовые операторы

Ряд операций выполняется над двоичными разрядми числа

Здесь важно понимать, как выглядит двоичное представление тех или иных чисел. В частности, число 4 в двоичном виде — 100, а число 15 — 1111

Есть следующие побитовые операторы (они применяются только к данным типов Int и Long):

  • shl: сдвиг битов числа со знаком влево

    val z = 3 shl 2     // z = 11 << 2 = 1100 
    println(z)          // z = 12
    val d = 0b11 shl 2
    println(d)          // d = 12
    

    В данном случае число сдвигается на два разряда влево, поэтому справа число в двоичном виде дополняется двумя нулями. То есть в двоичном виде 3 представляет 11. Сдвигаем
    на два разряда влево (дополняем справа двумя нулями) и получаем 1100, то есть в десятичной системе число 12.

  • shr: сдвиг битов числа со знаком вправо

    val z = 12 shr 2     // z = 1100 >> 2 = 11
    println(z)          // z = 3
    val d = 0b1100 shr 2
    println(d)          // d = 3
    

    Число 12 сдвигается на два разряда вправо, то есть два числа справа факически отбрасываем и получаем число 11, то есть 3 в десятичой системе.

  • ushr: сдвиг битов беззнакового числа вправо

    val z = 12 ushr 2     // z = 1100 >> 2 = 11
    println(z)          // z = 3
    
  • and: побитовая операция AND (логическое умножение или конъюнкция). Эта операция сравнивает соответствующие разряды двух чисел и возвращает единицу, если эти разряды обоих чисел равны 1.
    Иначе возвращает 0.

    val x = 5   // 101
    val y = 6   // 110
    val z = x and y     // z = 101 & 110 = 100
    println(z)          // z = 4
    
    val d = 0b101 and 0b110
    println(d)          // d = 4
    
  • or: побитовая операция OR (логическое сложение или дизъюнкция). Эта операция сравнивают два соответствуюших разряда обоих чисел и
    возвращает 1, если хотя бы один разряд равен 1. Если оба разряда равны 0, то возвращается 0.

    val x = 5   // 101
    val y = 6   // 110
    val z = x or y     // z = 101 | 110 = 111
    println(z)         // z = 7
    
    val d = 0b101 or 0b110
    println(d)          // d = 7
    
  • xor: побитовая операция XOR. Сравнивает два разряда и возвращает 1, если один из разрядов равен 1, а другой равен 0.
    Если оба разряда равны, то возвращается 0.

    val x = 5   // 101
    val y = 6   // 110
    val z = x xor y     // z = 101 ^ 110 = 011
    println(z)         // z = 3
    
    val d = 0b101 xor 0b110
    println(d)          // d = 3
    
  • inv: логическое отрицание или инверсия — инвертирует биты числа

    val b = 11  // 1011
    val c = b.inv()
    println(c)      // -12
    

НазадВперед

Запрещенные темы для публикаций

Clone

  • Метод сlone нужен для копирования объекта.
  • В методе clone присутствует защищенный модификатор доступа.
  • Для вызова метода clone объекту требуется реализация интерфейса Cloneable. В противном случае выбрасывается исключение CloneNotSupportedException.
  • Интерфейс Cloneable является маркерным, то есть методы не определяют интерфейс, а говорят классу об особом отношении.
  • Плюс такого интерфейса: можно копировать только объекты, доступные для клонирования.
  • Если какое-то поле объекта ссылается на другой объект, то делаем поверхностную копию. В ней копируется только адрес памяти, т.е. используется один и тот же объект.
  • При глубоком копировании происходит создание объекта и новое динамическое распределение памяти.

Не обращайте внимание на оператора try — к нему мы вернемся позже

JVM, JRE и JDK

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

Поток JVM, JRE и JDK 

А теперь подробнее:

  • JVM — виртуальная машина Java, выполняющая байт-код Java.
  • JVM можно загружать на разном железе. Байт-коды — это машинный язык JVM. Поэтому Java является самым портируемым языком. JVM — это некий объект, который и обеспечивает портируемость. Для разных операционных систем (Mac, Windows, Linux и т.д.) придуманы свои реализации JVM.
  • JRE — среда выполнения Java, достаточная для запуска программы.
  • JRE = JVM + файлы библиотеки/пакеты классов (Util, Lang, Math etc).
  • JDK — пакет средств разработки на Java. Нужен для написания, компиляции и выполнения программы.
  • JDK = JRE + инструменты, необходимые для разработки Java-программы.

Операция instanceof

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

Java

obj1 instanceof A

1 obj1 instanceofA

Возвращается
true, если
obj1  не
null  и является экземпляром класса
A  или экземпляром дочернего класса
A  или экземпляром класса, реализующего интерфейс
A.

Java

Object obj1 = new String(«test1»);
if (obj1 instanceof String) {
System.out.println(«YES»);
}

1
2
3
4

Objectobj1=newString(«test1»);

if(obj1 instanceofString){

System.out.println(«YES»);

}

Если левый операнд равен
null, то результатом будет
false. Код ниже выведет “NO”:

Java

Object obj1 = null;
if (obj1 instanceof String) {
System.out.println(«YES»);
} else {
System.out.println(«NO»);
}

1
2
3
4
5
6

Objectobj1=null;

if(obj1 instanceofString){

System.out.println(«YES»);

}else{

System.out.println(«NO»);

}

Класс Random

В качестве генератора псевдослучайных чисел можно также использовать класс java.util.Random, имеющий два
конструктора :

public Random();
public Random(long);

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

Метод Описание
boolean nextBoolean() получение следующего случайного значения типа boolean
double nextDouble() получение следующего случайного значения типа double
float nextFloat() получение следующего случайного значения типа float
int nextInt() получение следующего случайного значения типа int
int nextInt(int n) получение следующего случайного значения типа int в диапазоне от 0 до n
long nextLong() получение следующего случайного значения типа long
void nextBytes(byte[] buf) формирование массива из случайно генерируемых значений

Пример получения псевдослучайного целочисленного значения с использованием класса Random :

Random random = new Random();

int i = random.nextInt();

С классом Random алгоритм получения псевдослучайного числа такой же, как и у метода random класса
Math. Допустим, что нам необходимо получить случайное число в диапазоне , 100 включительно. В этом случае
код может выглядеть следующим образом :

int min = 5;
int max = 100;
int diff = max - min;
Random random = new Random();
int i = random.nextInt(diff + 1) + min;

Класс SecureRandom

В следующем примере формируется массив псевдослучайных значений типа byte :

SecureRandom random = new SecureRandom();
byte bytes[] = new byte;
random.nextBytes(bytes);

Этот же массив можно сформировать методом generateSeed :

 byte seed[] = random.generateSeed(8);

Пример использования SecureRandom представлен на странице
Симметричного шифрования.

Класс ThreadLocalRandom

В JDK 7 включен класс ThreadLocalRandom из многопоточного пакета
java.util.concurrent, который следует использовать для получения псевдослучайных
значений в многопоточных приложениях. Для получения экземпляра ThreadLocalRandom следует использовать
статический метод current() данного класса. Пример :

ThreadLocalRandom random = ThreadLocalRandom.current();

System.out.println("Random values : ");
System.out.println("boolean : " + random.nextBoolean());
System.out.println("int : "     + random.nextInt    ());
System.out.println("float : "   + random.nextFloat  ());
System.out.println("long : "    + random.nextLong   ());

System.out.println("int from 0 to 5 : "   + 
                                  random.nextInt(5));
System.out.println("long from 5 to 15 : " + 
                                  random.nextLong(5, 15));

3 String Pool

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

Во-первых, строки, заданные в коде, нужно все-таки где-то хранить. Код — это команды, а данные (тем более такие большие как строки) нужно хранить в памяти отдельно от кода. В коде фигурируют только ссылки на объекты-строки.

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

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

Как это примерно работает:

Код Работа с StringPoll

Именно поэтому переменные и будут хранить одинаковые ссылки.

Метод

Ну и самое интересное: вы можете программно добавить любую строку в . Для этого нужно просто вызвать метод у -переменной.

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

Код Примечание

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

Добавить комментарий

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

Adblock
detector