Типы данных в Java

Java различает примитивные типы и ссылочные типы. Примитивных всего восемь: byte, short, int, long, float, double, char, boolean. Их значения хранятся прямо в переменной — без обёрток и заголовков объекта. Всё остальное (String, массивы, любые ваши классы) — ссылочные типы: переменная хранит адрес объекта в куче.

Размер примитива одинаков на всех платформах — это принципиальное отличие от C/C++. int в Java всегда 32 бита, long всегда 64 бита, независимо от ОС и процессора.

Каждому примитиву соответствует класс-обёртка (Integer, Double, Boolean…) — он нужен, когда требуется объект: в коллекциях List<Integer>, при работе с дженериками, для null-значения.

Зачем это нужно

  • Правильно выбирать тип под задачу: byte для пина (0..127), int для счётчика, long для миллисекунд эпохи, double для расчётов.

  • Избегать переполнения и потери точности.

  • Понимать разницу между «значение» (примитив) и «ссылка» (объект) — это основа поведения присваивания и передачи в методы.

Пример 1. Все восемь примитивов

public class Primitives {
    public static void main(String[] args) {
        byte    b = 100;             // 8 бит, -128..127
        short   s = 30000;           // 16 бит, -32 768..32 767
        int     i = 2_000_000_000;   // 32 бита
        long    l = 9_000_000_000L;  // 64 бита, суффикс L
        float   f = 3.14f;           // 32 бита, суффикс f
        double  d = 3.141592653589793; // 64 бита
        char    c = 'Z';             // 16 бит, Unicode
        boolean flag = true;

        System.out.println(b);
        System.out.println(s);
        System.out.println(i);
        System.out.println(l);
        System.out.println(f);
        System.out.println(d);
        System.out.println(c);
        System.out.println(flag);
    }
}

Вывод:

100
30000
2000000000
9000000000
3.14
3.141592653589793
Z
true

Пример 2. Диапазоны значений

public class Ranges {
    public static void main(String[] args) {
        System.out.println("byte:  " + Byte.MIN_VALUE   + " .. " + Byte.MAX_VALUE);
        System.out.println("short: " + Short.MIN_VALUE  + " .. " + Short.MAX_VALUE);
        System.out.println("int:   " + Integer.MIN_VALUE + " .. " + Integer.MAX_VALUE);
        System.out.println("long:  " + Long.MIN_VALUE   + " .. " + Long.MAX_VALUE);
        System.out.println("float ~ 7 знач. цифр, double ~ 15-16.");
    }
}

Вывод:

byte:  -128 .. 127
short: -32768 .. 32767
int:   -2147483648 .. 2147483647
long:  -9223372036854775808 .. 9223372036854775807
float ~ 7 знач. цифр, double ~ 15-16.

Пример 3. Значения по умолчанию у полей

public class Defaults {
    static byte    b;
    static short   s;
    static int     i;
    static long    l;
    static float   f;
    static double  d;
    static char    c;
    static boolean flag;
    static String  text;

    public static void main(String[] args) {
        System.out.println("byte    = " + b);
        System.out.println("short   = " + s);
        System.out.println("int     = " + i);
        System.out.println("long    = " + l);
        System.out.println("float   = " + f);
        System.out.println("double  = " + d);
        System.out.println("char    = [" + c + "] (код " + (int) c + ")");
        System.out.println("boolean = " + flag);
        System.out.println("String  = " + text);
    }
}

Вывод:

byte    = 0
short   = 0
int     = 0
long    = 0
float   = 0.0
double  = 0.0
char    = [ ] (код 0)
boolean = false
String  = null

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

Пример 4. Неявное и явное приведение

public class Casting {
    public static void main(String[] args) {
        int i = 100;
        long l = i;           // неявное расширение int -> long
        double d = l;         // неявное long -> double
        System.out.println(l + " " + d);

        double pi = 3.99;
        int truncated = (int) pi;   // явное сужение, отбрасывает дробную часть
        System.out.println(truncated);

        int big = 130;
        byte b = (byte) big;        // выход за диапазон byte: -128..127
        System.out.println(b);
    }
}

Вывод:

100 100.0
3
-126

Расширение (от меньшего к большему) — автоматическое. Сужение требует явного (тип) и может потерять данные.

Пример 5. char как число

public class CharAsNumber {
    public static void main(String[] args) {
        char a = 'A';
        int code = a;            // 65
        char next = (char) (a + 1); // 'B'
        System.out.println(a + " -> " + code);
        System.out.println(next);

        for (char ch = 'A'; ch <= 'E'; ch++) {
            System.out.print(ch);
        }
        System.out.println();
    }
}

Вывод:

A -> 65
B
ABCDE

char — это беззнаковое 16-битное число (Unicode code unit). Над ним работают арифметические операции.

Пример 6. Обёртки и автоупаковка

import java.util.ArrayList;
import java.util.List;

public class Wrappers {
    public static void main(String[] args) {
        Integer x = 42;          // автоупаковка: int -> Integer
        int y = x;               // авторапаковка: Integer -> int

        List<Integer> pins = new ArrayList<>();
        pins.add(13);
        pins.add(12);
        pins.add(11);

        int sum = 0;
        for (Integer pin : pins) {
            sum += pin;          // авторапаковка
        }
        System.out.println("Сумма пинов: " + sum);
        System.out.println("MAX_VALUE: " + Integer.MAX_VALUE);
    }
}

Вывод:

Сумма пинов: 36
MAX_VALUE: 2147483647

Коллекции работают только с объектами, поэтому List<int> запрещён — используют List<Integer>.

Пример 7. Переполнение целых

public class Overflow {
    public static void main(String[] args) {
        int max = Integer.MAX_VALUE;
        int overflow = max + 1;
        System.out.println("max       = " + max);
        System.out.println("max + 1   = " + overflow);

        // Решение: использовать long
        long safe = (long) max + 1;
        System.out.println("safe long = " + safe);
    }
}

Вывод:

max       = 2147483647
max + 1   = -2147483648
safe long = 2147483648

При переполнении Java не выбрасывает исключение — значение «закольцовывается». Будьте внимательны при умножении больших int.

Пример 8. String — ссылочный тип

public class StringIsReference {
    public static void main(String[] args) {
        String a = "AlashEd";
        String b = a;            // b ссылается на ту же строку
        String c = new String("AlashEd");

        System.out.println(a.equals(b));   // true — содержимое
        System.out.println(a.equals(c));   // true — содержимое
        System.out.println(a == b);        // true — одна ссылка (литералы кешируются)
        System.out.println(a == c);        // false — new String — отдельный объект
    }
}

Вывод:

true
true
true
false

Для сравнения строк по содержимому всегда используйте equals, а не ==.

Подводные камни

Предупреждение

  • double не точен: 0.1 + 0.2 == 0.3 вернёт false. Для денег — BigDecimal.

  • Сужающее приведение молча обрезает: (byte) 200 даст -56, без предупреждения в рантайме.

  • Integer кешируется только в диапазоне -128..127: Integer.valueOf(200) == Integer.valueOf(200) вернёт false. Сравнивайте объекты через equals.

  • null в обёртке + авторапаковка → NullPointerException: Integer x = null; int y = x; — упадёт в рантайме.

  • Java не имеет беззнакового int: для 32-битных беззнаковых используйте Integer.toUnsignedLong или работайте с long.

  • char != строка: "A" — это String, 'A' — это char. System.out.println("A" + 1) даст "A1", а System.out.println('A' + 1) даст 66.

См. также

Примечание

Лицензия и источники

Техническое описание адаптировано из официальной документации Oracle Java Tutorials (https://docs.oracle.com/javase/tutorial/), Oracle Free Documentation License. Перевод на русский, примеры и пояснения — © AlashEd Wiki.