Typy danych w Javie określają, jakie wartości może przechować zmienna, ile zajmuje miejsca i jakie operacje można na niej wykonać. Java ma 8 typów prostych: byte, short, int, long, float, double, char i boolean, oraz typy referencyjne, do których należą String, tablice, klasy opakowujące i wszystkie klasy, które napiszecie sami. W testach automatycznych typ danych decyduje o tym, czy asercja na kwocie, liczniku albo czasie odpowiedzi sprawdza to, co myślicie, dlatego ta lekcja kończy się na kwotach w BigDecimal i cenach czytanych ze strony przez Selenium i Playwright.
To szósta lekcja kursu Javy dla testerów. Poprzednia była o komentarzach w kodzie, następna dotyczy operatorów matematycznych. Film pokazuje typy w IntelliJ IDEA; tekst dokłada zakresy, var, rzutowanie, autoboxing i komunikaty kompilatora. Cały kod skompilowaliśmy i uruchomiliśmy 11.10.2026 na OpenJDK 25.0.2 z opcją --release 25.
Spis treści
- Jakie typy danych w Javie są typami prostymi i jakie mają zakresy?
- Czym typy referencyjne różnią się od prostych?
- Kiedy używać var zamiast nazwy typu?
- Jak działa rzutowanie typów w Javie?
- Co to jest autoboxing i dlaczego 128 nie równa się 128?
- Jakie pułapki typów danych psują testy automatyczne?
- Jak sprawdzać kwoty w testach: BigDecimal, Selenium i Playwright?
- Jakie błędy kompilatora zobaczycie przy typach danych?
- Ćwiczenie: ile milisekund ma 30 dni?
- Co dalej po lekcji o typach danych?
Jakie typy danych w Javie są typami prostymi i jakie mają zakresy?
Typ prosty (prymitywny) przechowuje samą wartość, bez obiektu dookoła. Według specyfikacji języka Java (JLS, rozdział 4.2, wydanie dla Java SE 25 z 2025 roku) rozmiary i zakresy są stałe na każdym systemie operacyjnym i każdym procesorze: int ma zawsze 32 bity, niezależnie od tego, czy test uruchamiacie na laptopie, czy w kontenerze w CI. To różni Javę od języków, w których rozmiar typu zależy od platformy.
Zakresy nie musicie pamiętać, bo każda klasa opakowująca ma stałe MIN_VALUE i MAX_VALUE. Pierwsza część programu z tej lekcji wypisuje je wprost:
// 1. Zakresy typów prostych
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("char: " + (int) Character.MIN_VALUE + " .. " + (int) Character.MAX_VALUE);
System.out.println("float max: " + Float.MAX_VALUE);
System.out.println("double max: " + Double.MAX_VALUE);
byte: -128 .. 127
short: -32768 .. 32767
int: -2147483648 .. 2147483647
long: -9223372036854775808 .. 9223372036854775807
char: 0 .. 65535
float max: 3.4028235E38
double max: 1.7976931348623157E308
Cztery liczby, które warto znać na pamięć
8
typów prostych w Javie, reszta to typy referencyjne
2 147 483 647
największy int; o jeden więcej daje liczbę ujemną
-128..127
zakres wartości Integer, dla których == przypadkiem działa
0,1 + 0,2
w double daje 0.30000000000000004
Źródło: JLS Java SE 25, rozdziały 4.2 i 5.1.7; uruchomienie Quality Island 10.10.2026.
Czym typy referencyjne różnią się od prostych?
Zmienna typu referencyjnego nie przechowuje obiektu, tylko odniesienie do niego. Dwie zmienne mogą wskazywać ten sam obiekt albo dwa różne obiekty o tej samej zawartości, a zmienna referencyjna może mieć wartość null, czyli nie wskazywać niczego. Typ prosty null mieć nie może: int zawsze ma jakąś liczbę.
Najważniejszym typem referencyjnym w testach jest String. To klasa, nie typ prosty, choć literały w cudzysłowach wyglądają niewinnie. Obiekty String są niezmienne: metody takie jak trim() czy toUpperCase() zwracają nowy napis i nie zmieniają starego. Szczegóły napisów rozwija lekcja o łańcuchach znakowych. Tu wystarczy jedna reguła, która w testach wraca co tydzień:
// 6. String: == i equals
String status = "PASSED";
String zApi = new String("PASSED");
System.out.println("== " + (status == zApi) + ", equals " + status.equals(zApi));
== false, equals true
Operator == na typach referencyjnych porównuje adresy obiektów, a nie ich zawartość. Napis zbudowany w trakcie testu, odczytany z odpowiedzi API albo pobrany z przeglądarki przez getText() to zwykle nowy obiekt, więc == zwróci false mimo identycznej treści. Do porównania treści służy equals(), a w asercjach assertEquals(), które wywołuje equals() za Was.
Kiedy używać var zamiast nazwy typu?
Według JEP 286, wdrożonego w Javie 10 (2018), zmienną lokalną możecie zadeklarować słowem var, a kompilator sam ustali typ na podstawie wartości po prawej stronie. Typ jest ustalany raz, w czasie kompilacji: var nie robi z Javy języka dynamicznego i nie pozwala potem przypisać do tej samej zmiennej napisu zamiast liczby.
// 2. var: typ wybiera kompilator na podstawie wartości
var liczbaTestow = 128; // int
var czasMs = 1_250L; // long
var nazwa = "Logowanie"; // String
System.out.println(liczbaTestow + " " + czasMs + " " + nazwa);
128 1250 Logowanie
var działa tylko przy zmiennych lokalnych z wartością początkową, nie przy polach klasy ani parametrach metod. Dobrze sprawdza się przy długich nazwach typów, np. var oczekiwanie = new WebDriverWait(driver, Duration.ofSeconds(10));. Gorzej przy literałach liczbowych: z var limit = 30; nie widać od razu, że to int, a nie long, co przy liczeniu milisekund ma znaczenie, jak pokaże ćwiczenie na końcu.
Jak działa rzutowanie typów w Javie?
Rzutowanie to zamiana wartości jednego typu na inny. Rzutowanie rozszerzające, z mniejszego typu do większego (int do long, int do double), Java robi sama, bo nic nie ginie. Rzutowanie zawężające, z większego do mniejszego, musicie zapisać jawnie typem w nawiasie, bo część informacji może przepaść, a kompilator chce, żebyście podpisali, że o tym wiecie.
// 3. Rzutowanie
double srednia = 7.9;
int obcieta = (int) srednia; // 7, część ułamkowa znika
long duzo = 3_000_000_000L;
int przepelniona = (int) duzo; // -1294967296
System.out.println(obcieta + " " + przepelniona);
7 -1294967296
Oba wyniki są poprawne z punktu widzenia Javy i oba są typowym źródłem błędów. Rzutowanie (int) 7.9 nie zaokrągla, tylko obcina część ułamkową, więc średnia 7,9 staje się siódemką. Rzutowanie long 3 miliardy na int zostawia tylko dolne 32 bity, a to daje liczbę ujemną. Jeśli chcecie zaokrąglić, użyjcie Math.round(), a jeśli wartość może nie zmieścić się w int, Math.toIntExact() rzuci wyjątek zamiast po cichu zwrócić bzdurę.
Co to jest autoboxing i dlaczego 128 nie równa się 128?
Każdy typ prosty ma klasę opakowującą: Integer, Long, Double, Boolean i tak dalej. Java zamienia jedno na drugie automatycznie, co nazywa się autoboxingiem (z typu prostego do obiektu) i unboxingiem (w drugą stronę). Dzięki temu możecie wstawić int do listy List<Integer>, ale płacicie za to jedną pułapką:
// 5. Autoboxing i ==
Integer a = 127, b = 127;
Integer c = 128, d = 128;
System.out.println("127 == 127: " + (a == b));
System.out.println("128 == 128: " + (c == d));
System.out.println("128 equals: " + c.equals(d));
127 == 127: true
128 == 128: false
128 equals: true
Dlaczego 127 tak, a 128 nie
Według JLS (rozdział 5.1.7, Java SE 25) opakowanie liczby z zakresu od -128 do 127 zawsze daje ten sam obiekt, więc == zwraca true. Powyżej tego zakresu każde opakowanie może dać nowy obiekt i == porównuje dwa różne adresy. Test, który porównuje identyfikatory zamówień przez ==, przechodzi więc na danych testowych z małymi numerami i pada na środowisku, gdzie numery przekroczyły 127. To jeden z niewielu błędów, które naprawdę pojawiają się dopiero, gdy aplikacja ma więcej niż 127 klientów.
Drugą pułapką jest unboxing wartości null. Jeśli metoda zwraca Integer, a w bazie brakuje danych, przypisanie int wiersze = repo.liczbaWierszy(); skompiluje się bez problemu i rzuci NullPointerException dopiero w czasie testu.
Jakie pułapki typów danych psują testy automatyczne?
Przepełnienie int nie zgłasza żadnego błędu. Według JLS (rozdział 15.18.2) przy przepełnieniu wynik dodawania to dolne bity matematycznie poprawnej sumy, więc największy int powiększony o 1 staje się najmniejszym. Gdy wynik musi być poprawny albo żaden, użyjcie Math.addExact() i Math.multiplyExact(), które rzucają wyjątek:
// 4. Przepełnienie int
int max = Integer.MAX_VALUE;
System.out.println("MAX + 1 = " + (max + 1));
try {
Math.addExact(max, 1);
} catch (ArithmeticException e) {
System.out.println("addExact: " + e.getMessage());
}
MAX + 1 = -2147483648
addExact: integer overflow
Druga pułapka to ułamki binarne. Typy float i double zapisują liczby w systemie dwójkowym według standardu IEEE 754, a ułamka 0,1 nie da się w nim zapisać dokładnie, tak jak 1/3 nie da się zapisać dokładnie w systemie dziesiętnym. Stąd wynik, który zaskakuje każdego na początku nauki:
// 7. Kwoty: double kontra BigDecimal
System.out.println("0.1 + 0.2 = " + (0.1 + 0.2));
System.out.println(new BigDecimal(0.1));
0.1 + 0.2 = 0.30000000000000004
0.1000000000000000055511151231257827021181583404541015625
Druga linia pokazuje, co naprawdę siedzi w double zapisanym jako 0.1: konstruktor new BigDecimal(double) przepisuje ten przybliżony stan co do bitu. Kwoty w Javie twórzcie więc z napisu, new BigDecimal("0.1"), albo przez BigDecimal.valueOf(), nigdy z literału double. Trzecia pułapka to porównanie samych BigDecimal:
BigDecimal suma = new BigDecimal("0.1").add(new BigDecimal("0.2"));
System.out.println("BigDecimal: " + suma);
BigDecimal netto = new BigDecimal("99.99");
BigDecimal brutto = netto.multiply(new BigDecimal("1.23")).setScale(2, RoundingMode.HALF_UP);
System.out.println("brutto: " + brutto);
System.out.println("2.0 equals 2.00: " + new BigDecimal("2.0").equals(new BigDecimal("2.00")));
System.out.println("2.0 compareTo 2.00: " + new BigDecimal("2.0").compareTo(new BigDecimal("2.00")));
BigDecimal: 0.3
brutto: 122.99
2.0 equals 2.00: false
2.0 compareTo 2.00: 0
Według dokumentacji klasy BigDecimal (Java SE 25) metoda equals() uznaje dwie liczby za równe tylko wtedy, gdy mają tę samą wartość i tę samą skalę, czyli liczbę miejsc po przecinku. Dla equals() 2.0 i 2.00 to różne liczby, dla compareTo() równe. W asercjach na kwotach albo ujednolicajcie skalę przez setScale(2, RoundingMode.HALF_UP), albo porównujcie przez compareTo().
Jak sprawdzać kwoty w testach: BigDecimal, Selenium i Playwright?
Tu teoria spotyka się z codzienną pracą testera automatyzującego. Cena na stronie sklepu to dla przeglądarki tekst, np. „1 229,99 zł”. W Selenium odczytacie go metodą getText(), w Playwright for Java metodą textContent() albo innerText() lokatora. Zanim porównacie go z oczekiwaną kwotą, trzeba go zamienić na BigDecimal, a po drodze czeka pułapka, której nie widać gołym okiem.
package pl.qi;
import java.math.BigDecimal;
public final class Ceny {
private Ceny() {
}
/** Zamienia tekst ceny ze strony, np. "1 229,99 zł", na BigDecimal. */
public static BigDecimal zTekstu(String tekst) {
String liczba = tekst
.replace("zł", "")
.replaceAll("[\\s\\u00A0\\u202F]", "") // spacje, także twarde
.replace(',', '.');
return new BigDecimal(liczba);
}
}
package pl.qi;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Locale;
import org.junit.jupiter.api.Test;
class CenyTest {
@Test
void cenaZeStronyToKwotaZDwomaMiejscami() {
String zeStrony = "1 229,99 zł"; // tak zwraca getText() albo textContent()
BigDecimal cena = Ceny.zTekstu(zeStrony);
assertEquals(0, new BigDecimal("1229.99").compareTo(cena));
}
@Test
void bruttoLiczoneNaBigDecimal() {
BigDecimal netto = new BigDecimal("99.99");
BigDecimal brutto = netto.multiply(new BigDecimal("1.23"))
.setScale(2, RoundingMode.HALF_UP);
assertEquals(new BigDecimal("122.99"), brutto);
}
@Test
void doubleWymagaTolerancji() {
double suma = 0.1 + 0.2;
assertNotEquals(0.3, suma);
assertEquals(0.3, suma, 0.000001);
}
@Test
void formatPolskiUzywaTwardejSpacji() {
String sformatowana = NumberFormat.getCurrencyInstance(Locale.of("pl", "PL"))
.format(new BigDecimal("1229.99"));
assertNotEquals("1 229,99 zł", sformatowana); // zwykła spacja w oczekiwanym
assertEquals(new BigDecimal("1229.99"), Ceny.zTekstu(sformatowana));
}
}
CenyTest ✔
├─ cenaZeStronyToKwotaZDwomaMiejscami() ✔
├─ bruttoLiczoneNaBigDecimal() ✔
├─ doubleWymagaTolerancji() ✔
└─ formatPolskiUzywaTwardejSpacji() ✔
4 tests successful, 0 failed
Ostatni test opisuje błąd, który w naszej praktyce na szkoleniach z automatyzacji wraca regularnie: oczekiwana cena wpisana w kodzie ma zwykłą spację, a strona albo NumberFormat dla polskiego ustawienia regionalnego używa spacji twardej. W naszym uruchomieniu JDK 25 sformatował 1229,99 jako ciąg ze znakiem U+00A0 zarówno między tysiącami, jak i przed „zł”. Na ekranie oba napisy wyglądają identycznie, a assertEquals na tekstach pada. Dlatego porównujcie liczby, nie napisy, i usuwajcie wszystkie rodzaje spacji przy parsowaniu. Jeśli Wasz framework ma jedną metodę pomocniczą do cen, jak Ceny.zTekstu powyżej, ta pułapka zatrzyma Was tylko raz, a nie w każdym teście z osobna.
BigDecimal tworzony z napisu, skala 2, RoundingMode.HALF_UP; albo long w groszach, jak w lekcji o komentarzach.
double wystarczy, ale w asercji zawsze z tolerancją: assertEquals(0.3, wynik, 0.000001).
int do liczby wierszy, long do identyfikatorów z bazy; porównanie Long przez equals(), nie ==.
Więcej o samych asercjach, także tych w Selenium i TestNG, znajdziecie we wpisie asercje w testach automatycznych. Jeśli zastanawiacie się, czy w 2026 roku warto uczyć się Javy pod Selenium, przeczytajcie na portalu Strefa QA analizę co liczby z 2025 roku mówią o Selenium.
Jakie błędy kompilatora zobaczycie przy typach danych?
Wszystkie komunikaty poniżej wywołaliśmy celowo w javac 25 z angielskimi komunikatami, bo takie zobaczycie w logach Maven i Jenkins. Warto je umieć przeczytać, bo każdy z nich wskazuje dokładnie miejsce i powód. W Quality Island na szkoleniach z podstaw Javy te właśnie komunikaty pojawiają się w pierwszej godzinie ćwiczeń, więc nie traktujcie ich jak porażki, tylko jak podpowiedź kompilatora dla Was.
Ćwiczenie: ile milisekund ma 30 dni?
Zadanie: test sprawdza, czy token wygasa po 30 dniach, i porównuje czas w milisekundach. Policzcie liczbę milisekund w 30 dniach w Javie trzema sposobami z programu poniżej i przewidźcie wynik każdej linii, zanim uruchomicie kod.
public class Cwiczenie {
public static void main(String[] args) {
int zle = 30 * 24 * 60 * 60 * 1000;
long dobrze = 30L * 24 * 60 * 60 * 1000;
long nadalZle = 30 * 24 * 60 * 60 * 1000;
System.out.println(zle);
System.out.println(dobrze);
System.out.println(nadalZle);
}
}
Rozwiązanie
-1702967296
2592000000
-1702967296
30 dni to 2 592 000 000 milisekund, a największy int to 2 147 483 647, więc pierwszy wynik przepełnia się do liczby ujemnej. Najciekawsza jest trzecia linia: zmienna jest typu long, a wynik i tak jest zły, bo całe mnożenie wykonało się na liczbach int i dopiero przepełniony wynik został zamieniony na long. Wystarczy, że pierwszy czynnik ma przyrostek L, jak w drugiej linii, a całe wyrażenie liczy się w long. W prawdziwym teście użyjcie Duration.ofDays(30).toMillis() i problem znika.
Co dalej po lekcji o typach danych?
W następnej lekcji typy danych spotykają się z działaniami: dzielenie całkowite, reszta z dzielenia, inkrementacja i dzielenie przez zero, które dla int i double kończy się zupełnie inaczej. Jeśli chcecie przerobić typy i resztę podstaw z trenerem, na przykładach z testów, zobaczcie szkolenie Programowanie w języku Java dla testerów oprogramowania (2 dni, 2 212 zł netto według karty szkolenia, odczyt 10.10.2026). Po podstawach naturalnym krokiem jest automatyzacja testów z Playwright w Javie albo automatyzacja testów Selenium WebDriver, a zespołom, które chcą zlecić budowę testów, pomagamy w ramach usługi automatyzacji testów.
Co zabrać z tego artykułu
01Java ma 8 typów prostych o stałych rozmiarach (JLS 4.2) i typy referencyjne, do których należy String.
02Rzutowanie zawężające obcina, a nie zaokrągla; przepełnienie int nie zgłasza błędu, chyba że użyjecie Math.addExact().
03== porównuje adresy obiektów: dla String, Integer i Long używajcie equals(); Integer 127 przechodzi, 128 nie.
04Kwoty w BigDecimal tworzonym z napisu; porównanie przez compareTo() albo po setScale(2).
05Cena czytana ze strony może mieć twardą spację U+00A0: parsujcie do BigDecimal i porównujcie liczby, nie napisy.
Źródło: JLS Java SE 25, rozdziały 4.2, 5.1.7, 15.18.2; JEP 286; dokumentacja BigDecimal; uruchomienie Quality Island na OpenJDK 25.0.2 i JUnit 6.1.3, 11.10.2026.
Piszecie asercje na kwotach i licznikach? Na szkoleniu Java dla testerów przerabiacie typy danych, kolekcje i asercje na przykładach z prawdziwych testów.
Powiązane na blogu Quality Island
- Komentarze w kodzie Java, lekcja 5 kursu
- Operatory matematyczne w Javie, lekcja 7 kursu
- Łańcuchy znakowe w Javie
- Asercje w testach automatycznych: Selenium, JUnit, TestNG
- Jaki język programowania wybrać do automatyzacji testów
Pełna lista źródeł
- Oracle, The Java Language Specification, Java SE 25, rozdział 4 (4.2 Primitive Types and Values), 2025.
- Oracle, The Java Language Specification, Java SE 25, rozdział 5 (5.1.7 Boxing Conversion), 2025.
- Oracle, The Java Language Specification, Java SE 25, rozdział 15 (15.18.2 Additive Operators), 2025.
- OpenJDK, JEP 286: Local-Variable Type Inference, Java 10, 2018.
- Oracle, BigDecimal, dokumentacja API Java SE 25, 2025.
- Oracle, The Java Tutorials: Primitive Data Types, odczyt 2026.
- David Goldberg, What Every Computer Scientist Should Know About Floating-Point Arithmetic, ACM Computing Surveys 1991, przedruk Oracle.
- Selenium, Information about web elements (getText), dokumentacja 2026.
- Playwright, Locator, Playwright for Java (textContent, innerText), dokumentacja 2026.
- Quality Island, uruchomienie przykładów z tej lekcji na OpenJDK 25.0.2 (opcja
--release 25) i JUnit 6.1.3, 11.10.2026.



