Core Java. Лекция 2

Примитивные типы. Управление выполнением. Операторы. Массивы.

Иван Пономарёв, КУРС/МФТИ

Демо

  • Hello, World!

  • New Maven Project

  • New package

  • New class

  • psvm — sout

  • Манифест (runnable jar)

Ключевые слова языка (keywords) [JLS17, 3.9]

abstract   continue   for          new         switch
assert     default    if           package     synchronized
boolean    do         goto         private     this
break      double     implements   protected   throw
byte       else       import       public      throws
case       enum       instanceof   return      transient
catch      extends    int          short       try
char       final      interface    static      void
class      finally    long         strictfp    volatile
const      float      native       super       while
_ (underscore)

Null and Boolean Literals

             null       true        false

Restricted Keywords & Identifiers

(Java 9)
open     module      requires    transitive    exports
opens    to          uses        provides      with

(Java 10)
var

(Java 14)
yield    record

(Java 15)
sealed   non-sealed  permits

Комментарии

/**
 * This is the first sample program in Core Java Chapter 3
 * @version 1.01 1997-03-22
 * @author Gary Cornell
 */
public class FirstSample
{
  public static void main(String[] args)
  {
  /*multiline
  comment*/
  System
    .out //single-line comment
    .println("We will not use 'Hello, World!'");
  }
}

Типы

  • Value

    • Примитивные

      • byte, short, int, long

      • char

      • float, double,

      • boolean

  • Reference

    • Массивы

    • Объекты

    • null, будь он неладен

Целочисленные типы

Type

Storage

Range

byte

1 byte

–128 to 127

short

2 bytes

–32,768 to 32,767

int

4 bytes

–2,147,483,648 to 2,147,483, 647

long

8 bytes

–9,223,372,036,854,775,808 to 9,223,372,036,854,775,807

Разновидности целочисленных литералов

  • Long 100L

  • Underscores 1_000_000

  • Hexadecimal 0xCAFEBABE

  • Binary 0b0101

  • Octal 010

Типы с плавающей запятой

Type

Storage

Range

float

4 bytes

Approximately ±3.40282347E+38F (6–7 significant decimal digits)

double

8 bytes

Approximately ±1.79769313486231570E+308 (15 significant decimal digits)

Литералы для типов с плавающей запятой

  • 2.998e8

  • 1.0 (1.)

  • 3.14F

  • 0x1.0p-3 (0.125, 2 в минус третьей степени)

  • Double.POSITIVE_INFINITY

  • Double.NEGATIVE_INFINITY

  • Double.NaN

Осторожней с плавающей запятой!

  • System.out.println(2.0 - 1.1) выводит 0.8999999999999999

  • НИКОГДА НЕ СЧИТАЙТЕ ДЕНЬГИ DOUBLE-ОМ!

  • System.out.println(2.0 / 0.0) выводит 'Infinity` (но 2 / 0 — divizion by zero)

  • System.out.println(0.0 / 0.0) выводит 'NaN`

  • Сравнение с бесконечностью и NaN не работает, надо проверять с помощью Double.isNaN, Double.isInfinite.

  • strictfp

Приводимость числовых типов

casting.png

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

double x = 9.997F; //float в double неявно
int nx = (int) x; //9
nx = (int) Math.round(x); //10

Тип char

  • 16 bit

  • code unit in the UTF-16 encoding

  • Не всегда целый символ (хотя почти всегда)!

  • Осторожней с Unicode, используйте String

Литералы char

  • 'ы'

  • '\u03C0' (греческая π)

  • '\'' (escaped single quote)

  • Ловушка Unicode Escape: // Look inside c:\users

Строковые литералы

String s = "Hello, world!";

//Before Java 13
String txt =
      "Some\n" +
      "  Nested\n" +
      "    Text\n" +
      "  Is\n" +
      "Here\n";

Some
  Nested
    Text
  Is
Here

Text Blocks (Java 13+)

    String txt = """
      Some
        Nested
          Text
        Is
      Here
      """;

//Максимальное число пробелов впереди убрано:
Some
  Nested
    Text
  Is
Here

Escape sequences

Escape sequence

Name

Unicode Value

\b

Backspace

\u0008

\t

Tab

\u0009

\n

Linefeed

\u000a

\r

Carriage return

\u000d

\"

Double quote

\u0022

\'

Single quote

\u0027

\\

Backslash

\u005c

\s

intentional whitespace (Java 13+)

\<line-terminator>

line continuation (Java 13+)

Тип boolean

  • true и false

  • В отличие от C и многих других языков, целые числа не сводятся автоматически к boolean

  • Избегаем ошибок вида if (x = 0) {…​

Определение переменных и их область видимости

// декларация
double salary;
int vacationDays;
long earthPopulation;
boolean done;

// не очень приветствуется
int i, j;

// технически можно, но . . .
int суммаНДФЛ;

Инициализация переменных

int vacationDays;
System.out.println(vacationDays); //COMPILATION ERROR
  // variable not initialized

Возможные способы инициализации:

int vacationDays;
vacationDays = 12;
int vacationDays = 12;

Ключевое слово final (константы)

  • final используется в двух смыслах:

    • запрещает изменять значение

    • запрещает переопределять методы/классы

final int a;
...
a = 42; // инициализация

...
a = 43; // compilation error:
  // variable a might already have been initialized

Область видимости

  • Место определения: класс, метод, блок

  • Нет ничего за пределами класса («глобальных» переменных)!

int n;
. . .
{
  int k;
  int n; // Error--can't redefine n in inner block
  . . .
} // k is only defined up to here

Вывод типов (type inference) при создании переменных (Java 10+)

var i = 1; // i is of int type

Вывод типов

//BEFORE JAVA 10
URL codefx = new URL("http://codefx.org");
URLConnection connection = codefx.openConnection();
Reader reader = new BufferedReader(
    new InputStreamReader(connection.getInputStream()));
//AFTER JAVA 10
var codefx = new URL("http://codefx.org");
var connection = codefx.openConnection();
var reader = new BufferedReader(
    new InputStreamReader(connection.getInputStream()));

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

  • Арифметические: +, -, *, /, %

  • Деление работает как целочисленное, если оба аргумента целочисленные

  • Унарные + и -.

Сдвиги

5

0000 …​ 0000 0101

5 << 1 == 10

0000 …​ 0000 1010

5 >> 1 == 2

0000 …​ 0000 0010

-5

1111 …​ 1111 1011

-5 << 1 == -10

1111 …​ 1111 0110

-5 >> 1 == -3

1111 …​ 1111 1101

старший (знаковый) бит сохраняется

-5 >>> 1 == 2147483645

0111 …​ 1111 1101

старший (знаковый) бит заполняется нулём

Не перемудрите со сдвигами!!

5 >> 32 == ?

Не перемудрите со сдвигами!!

5 >> 32 == 5

  • для int используются только 5 младших бит второго операнда (0..31),

  • для long используются только 6 младших бит второго операнда (0..63)

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

  • &, |, ^, ~

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

  • < <= > >= instanceof

  • == !=

Булевские операторы

  • Без короткого замыкания: &, |, ^, ! (вместо тильды — восклицательный знак!)

  • С коротким замыканием &&, ||.

x != 0 && 1 / x > x + y // no division by 0

Оператор присвоения

int x;
System.out.println(x = 42); //печатает 42

Пример использования:

while ((n = readValue()) != null)
  ... //делаем что-то с n

Пре/пост инкремент/декремент, присвоение с изменением

  • a++, ++a

  • a--, --a

  • +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, >>>=

  • ВНИМАНИЕ: может показаться, что эти операции атомарны. Но это не так.

Тернарный оператор

x < y ? x : y

Операторы в порядке убывания приоритета

Operators

Associativity

[] . () (method call)

Left to right

! ~ ++ -- + (unary) - (unary) () (cast) new

Right to left

* / %

Left to right

+ -

Left to right

<< >> >>>

Left to right

< <= > >= instanceof

Left to right

== !=

Left to right

&#8203;Операторы в порядке убывания приоритета (продолжение)&#8203;

Operators

Associativity

&

Left to right

^

Left to right

|

Left to right

&&

Left to right

||

Left to right

//У конъюнкции выше приоритет, чем у дизъюнкции, поэтому
a || b && c
// есть по сути
a || (b && c)

&#8203;Операторы в порядке убывания приоритета (продолжение)&#8203;

Operators

Associativity

? :

Right to left

= += -= *= /= %= &= |= ^= <<= >>= >>>=

Right to left

//Since += associates right to left, the expression
a += b += c
//means
a += (b += c)
//That is, the value of b += c
// (which is the value of b after the addition)
//is added to a.

А как же оператор «запятая»?

  • А его нет!

  • Можно несколько statement-ов перечислить через запятую в блоке for(…​), или в блоке try(…​)

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

if (yourSales >= target)
  performance = "Satisfactory";
if

if + block

if (yourSales >= target)
{
  performance = "Satisfactory";
  bonus = 100;
}
ifblock

if + block + else

if (yourSales >= target) {
  performance = "Satisfactory";
  bonus = 100 +
    0.01 * (yourSales - target);
} else {
  performance = "Unsatisfactory";
  bonus = 0;
}
ifelseblock

else группируется с ближайшим if

/* Лучше добавить фигурные скобки!!*/

if (x <= 0)
  if (x == 0)
    sign = 0;
  else
    sign = -1;
sign

Цепочки else if

if (sales >= 2 * target) {
 performance = "Excellent";
 bonus = 1000;
} else if (sales>=1.5*target) {
 performance = "Fine";
 bonus = 500;
} else if (sales >= target) {
 performance = "Satisfactory";
 bonus = 100;
} else {
 fireThisPerson();
}
elif

switch

Та же ерунда, что и в C/C++:

switch (choice)
{
  case 1:
    . . .
    break;
  case 2:
    . . .
    break;
  default:
    . . .
    break;
}
swibr

switch-case особенности:

  • Не забываем break-и (утилиты типа Checkstyle напоминают), иначе выполняем всё до конца switch, как в C!

  • switch бывает: по целому, по char-у, по String-у (Java 7+) и по enum-у.

Switch Expressions

  • JDK 12 & 13 — Preview Feature

  • JDK 14 — Standard Feature

int numLetters = switch(day) {
  case MONDAY, FRIDAY, SUNDAY -> 6;
  case TUESDAY -> 7;
  case SATURDAY -> 8;
  default -> 9;
}

Switch in Java 14+: no Fallthrough

Expression

Statement

int numLetters = switch(day) {
 case MONDAY, FRIDAY, SUNDAY -> 6;
 case TUESDAY -> 7;
 case THURSDAY, SATURDAY -> 8;
 default -> 9;
}
switch(day) {
 case MONDAY, FRIDAY, SUNDAY ->
  numLetters = 6;
 case TUESDAY -> {
  logger.info("Tuesday");
  numLetters = 7;
 }
 case THURSDAY, SATURDAY ->
  numLetters = 8;
  //Need not be exhaustive:
  //no WEDNESDAY
}

Switch in Java 14+: with Fallthrough

Expression

Statement (Good Old C-style Switch)

int numLetters = switch(day) {
  case MONDAY, FRIDAY, SUNDAY:
    yield 6;
  case TUESDAY:
    logger.info("Tuesday");
    yield 7;
  case THURSDAY:
    logger.info("Thursday");
  case SATURDAY:
    yield 8;
  default:
    yield 9;
}
switch(day) {
  case MONDAY, FRIDAY, SUNDAY:
    numLetters = 6;
    break;
  case TUESDAY:
    logger.info("Tuesday");
    numLetters = 7;
    break;
  case THURSDAY:
    logger.info("Thursday");
  case SATURDAY:
    numLetters = 8;
    break;
  default:
    numLetters = 9;
}

Массивы

  • Из любого типа можно построить массив данного типа.

  • Длина массива может быть определена в runtime, но после создания не может быть изменена.

  • Массив аллоцируется в куче и передаётся по ссылке.

  • Массивы проверяют тип данных (ArrayStoreException) и границы (ArrayIndexOutOfBoundsException) в run-time.

  • Правда жизни: скорее всего, вы не будете использовать массивы в современном коде.

Декларирование и инициализация массива

  • Два варианта:

    • int[] a

    • int a[] — не делайте так

  • Инициализация:

    • int[] a = new int[100];

    • int[] a = {1, 3, 5};

    • анонимный массив: foo(new int[] {2, 4, 6});

&#8203;Массивы передаются по ссылке&#8203;

int[] luckyNumbers = smallPrimes;
luckyNumbers[5] = 12; // теперь smallPrimes[5] тоже 12

luckyNumbers = Arrays.copyOf(luckyNumbers, 2 * luckyNumbers.length);
//теперь luckyNumbers это отдельный массив
//и он стал в два раза длиннее

&#8203;Массивы ковариантны&#8203;

 String[] a = new String[1];
 //компилируется. ведь строка это объект,
 //и потому почему б не считать массив строк массивом объектов?
 Object[] b = a;
 //runtime ArrayStoreException
 b[0] = 5;

(Всё это страшно не совместимо с дженериками и коллекциями, которые пытаются ловить ошибки типов во время компиляции, но об этом речь впереди.)

«Многомерные» массивы

На самом деле их нет, но есть массивы массивов

int[][] magicSquare =
{
 {16, 3, 2, 13},
 {5, 10, 11, 8},
 {9, 6, 7, 12},
 {4, 15, 14, 1}
};

/* magicSquare[1][2] == ?? */
twodim.png

Треугольные матрицы

double[][] odds = new double[ROWNUM][];
for (int n = 0; n < ROWNUM; n++)
  odds[n] = new int[n + 1];

for (int n = 0; n < ROWNUM; n++)
 for (int k = 0; k <= n; k++){
   /* compute lotteryOdds*/
   . . .
   odds[n][k] = lotteryOdds;
 }
triangle.png

Цикл while: то же, что и в C

while (balance < goal) {
 balance += payment;
 double interest =
  balance * interestRate / 100;
 balance += interest;
 years++;
}
System.out.println(
 years + " years.");
while

То же, да не то же!

final boolean flag = false;
. . .
while (flag) {
   . . . //не скомпилируется, unreachable code
}

do while: цикл с постусловием

do {
  balance += payment;
  double interest =
    balance * interestRate / 100;
  balance += interest;
  years++;
  // print current balance
  // ask if ready to retire and get input
} while (input.equals("N"));
dowhile

Правда жизни: do-while бывает нужен очень, ОЧЕНЬ редко.

Циклы for

Снова всё то же, что и в C (по сути, сокращение цикла while):

for (int i = 1; i <= 10; i++)
  System.out.println(i);

И даже, хотя оператора «запятая» нет, можно так (но не нужно):

for (int i = 1; i <= 10; i++, j++)
  System.out.println(i);

Видимость переменной цикла

for (int i = 1; i <= 10; i++)
{
. . .
}
// i no longer defined here
int i;
for (i = 1; i <= 10; i++)
{
. . .
}
// i is still defined here

Использование break и continue

Прерывание цикла целиком:

while (years <= 100) {
  balance += payment;
  double interest = balance * interestRate / 100;
  balance += interest;
  if (balance >= goal) break;
  years++;
}

Переход к следующему циклу:

Scanner in = new Scanner(System.in);
while (sum < goal) {
  System.out.print("Enter a number: ");
  n = in.nextInt();
  if (n < 0) continue;
  sum += n; // not executed if n < 0
}

У нас нет goto, но есть метки для break и continue!

Scanner in = new Scanner(System.in);
int n;
// label is here!
read_data: while (. . .) {
  . . .
  for (. . .) {
    System.out.print("Enter a number >= 0: ");
    n = in.nextInt();
    if (n < 0)
      break read_data;
      // break out of read_data loop
    . . .
   }
}
if (n < 0) {
  // deal with bad situation
} else {
  // carry out normal processing
}