KezdőOlvasási idő: ~19 perc

Szintaxis és alap típusok

Primitív típusok, wrapper osztályok, változók, scope, életciklus, alap kulcsszavak és típuskonverzió

A Java nyelv alapkövei: primitív típusok, wrapper osztályok, változók, hatókör, életciklus és típuskonverzió.


1. Definíció

Mi ez?

A Java egy erősen típusos (strongly typed) nyelv, ami azt jelenti, hogy minden változónak deklarálni kell a típusát (vagy a var kulcsszóval ki kell következtetnie a fordítónak). A nyelv alapjait a primitív típusok és az azokat becsomagoló wrapper osztályok adják.

Miért létezik?

  • Típusbiztonság: a fordító compile-time elkapja a típushibákat
  • Teljesítmény: a primitív típusok közvetlenül a stack-en tárolódnak, nem kell heap allokáció
  • Platformfüggetlenség: a típusok mérete rögzített minden platformon (pl. int mindig 32 bit)

Hol helyezkedik el?

Ez a Java nyelv legalapvetőbb szintje — minden más (OOP, Collections, Streams) erre épül.


2. Alapfogalmak

2.1 Primitív típusok

A Java 8 primitív típust tartalmaz:

Típus Méret Alapértelmezett Tartomány Felhasználás
byte 8 bit 0 -128 — 127 Memória-takarékos tömbök, I/O
short 16 bit 0 -32 768 — 32 767 Ritkán használt
int 32 bit 0 -2³¹ — 2³¹-1 Alapértelmezett egész típus
long 64 bit 0L -2⁶³ — 2⁶³-1 Nagy számok, timestamp
float 32 bit 0.0f ~±3.4×10³⁸ Ritkán, inkább double
double 64 bit 0.0d ~±1.7×10³⁰⁸ Alapértelmezett tizedes típus
char 16 bit '\u0000' 0 — 65 535 Unicode karakter
boolean ~1 bit* false true/false Logikai érték

*A boolean mérete implementáció-függő: a JVM általában int-ként (32 bit) tárolja a stack-en.

Fontos szabályok:

  • Primitívek nem lehetnek null
  • Primitívek nem használhatók generics-ben (List<int> ❌, List<Integer> ✅)
  • Az int literálok alapértelmezetten int típusúak, a tizedes literálok double típusúak

2.2 Wrapper osztályok

Minden primitívnek van egy megfelelő wrapper osztálya:

Primitív Wrapper Cache tartomány
byte Byte -128 — 127 (teljes)
short Short -128 — 127
int Integer -128 — 127
long Long -128 — 127
float Float nincs cache
double Double nincs cache
char Character 0 — 127
boolean Boolean TRUE / FALSE (mindkettő)

Autoboxing/Unboxing:

Integer x = 42;        // autoboxing: int → Integer
int y = x;             // unboxing: Integer → int
Integer z = null;
int w = z;             // ⚠️ NullPointerException!

Integer Cache (-128 — 127):

Integer a = 127;
Integer b = 127;
System.out.println(a == b);    // true  (cache-ből jön)

Integer c = 128;
Integer d = 128;
System.out.println(c == d);    // false (új objektumok!)
System.out.println(c.equals(d)); // true  (érték összehasonlítás)

2.3 Változók, scope, lifecycle

Változó típusok:

Típus Deklaráció helye Alapértelmezett Élettartam
Lokális változó Metóduson belül ❌ Nincs (kötelező inicializálni) Blokk vége
Instance változó Osztályban (nem static) ✅ Van Objektum GC-ig
Class változó static ✅ Van Program vége
Paraméter Metódus szignatúra Hívó adja Metódus vége

Scope szabályok:

public void example() {
    int x = 10;            // scope: egész metódus
    if (x > 5) {
        int y = 20;        // scope: csak ez az if blokk
        System.out.println(x + y); // OK
    }
    // System.out.println(y); // ❌ Compile error! y itt nem látható
}

2.4 Type casting

Implicit (widening) — automatikus, veszteségmentes:

byte → short → int → long → float → double
char → int

Explicit (narrowing) — kézi, adatvesztes lehet:

double d = 9.78;
int i = (int) d;    // i = 9 (csonkítás, NEM kerekítés!)

int big = 130;
byte b = (byte) big; // b = -126 (overflow!)

Speciális szabályok:

  • float és double közötti konverzió precíziós veszteséggel járhat
  • intfloat konverziónál is lehet veszteség (float csak 24 bit mantissza)

2.5 Literálok, alapértékek és fordítói következtetés

A literálok használata mindennapi Java-fejlesztői rutin, és meglepően sok apró hibaforrást is tartalmazhat.

  • Az egész literálok, például a 42, alapértelmezetten int típusúak
  • A long literálhoz L utótag szükséges: 42L
  • A lebegőpontos literálok, például a 3.14, alapból double típusúak
  • A float literálhoz f utótag kell: 3.14f
  • Az alsóvonás javítja az olvashatóságot: 1_000_000
  • Bináris, oktális és hexadecimális literálok is vannak: 0b1010, 077, 0xFF

A fordító bizonyos esetekben konstans-kiértékelést és tartományellenőrzést is végez. Ezért a byte b = 10; lefordul, de a byte b = 1000; már nem.

Az alapértékek objektum-inicializáláskor szintén fontosak:

  • az instance mezők automatikusan kapnak default értéket
  • a lokális változók soha nem kapnak
  • a referencia típusok default értéke null
  • a numerikus primitíveké 0 / 0.0
  • a boolean default értéke false

2.6 Alap Java kulcsszavak — gyors térkép

Az első Java anyagokban sok kulcsszó újra és újra előjön, de kezdőként nehéz látni, hogy melyik mire való. Az alábbi lista egy gyors mentális térkép: nem minden részlet itt mélyül el, de ettől már érthetőbb lesz a szintaxis.

  • public — bárhonnan elérhető tag vagy típus; tipikusan publikus API-hoz használjuk.
  • private — csak az adott osztályból érhető el; tipikusan belső állapot védésére kell.
  • protected — csomagon belül és alosztályból elérhető; öröklési API-knál jelenik meg.
  • class — osztály deklaráció; objektumtípust definiál.
  • new — új objektum példányosítása; heap allokációt indít.
  • this — az aktuális objektum referenciája; mezőelérésre és constructor chainingre jó.
  • static — osztályszintű tag, nem példányhoz kötött; utility és konstans use case-eknél tipikus.
  • final — egyszeri hozzárendelés vagy továbböröklés/felülírás tiltása; invariánsoknál és konstansoknál gyakori.
  • abstract — nem teljes implementáció, amit leszármazott fejez be.
  • extends — osztály örököl egy másik osztályból.
  • implements — osztály interfész-szerződést valósít meg.
  • interface — viselkedési szerződés; absztrakcióhoz és polymorphismhoz használjuk.

Hasznos kezdő szabályok:

  • A mezők legtöbbször private, a kifelé adott API gyakran public.
  • A new létrehozza az objektumot, a konstruktor pedig inicializálja.
  • A static és az instance világát ne keverd: static kontextusban nincs this.
  • A final nem mindig jelent teljes immutabilitást; referenciát rögzíthet, de a mögöttes objektum még lehet mutable.
public class User {
    private final String name;

    public User(String name) {
        this.name = name;
    }

    public static User guest() {
        return new User("guest");
    }
}

Ebben a rövid példában egyszerre jelenik meg a public, private, final, this, static, new és class kulcsszó. A későbbi OOP fejezetek ezeket mélyebben is szétbontják, de az alap szerepük már itt látszik.


3. Gyakorlati használat

Mikor mit használj?

Szituáció Ajánlás Miért
Egész számok általában int Alapértelmezett, leggyakoribb
Pénzügyi számítások BigDecimal ⚠️ NEM double! Pontossági hibák!
Kollekcióban szám Integer (wrapper) Generics követelmény
Nagy adathalmaz, memória fontos primitív tömbök Wrapper 16+ byte/elem overhead
Boolean flag boolean primitív Egyszerű, nincs null-kérdés
Nullable boolean (DB-ből) Boolean wrapper null = "nem tudjuk"

Mikor NE használd?

  • double-t pénzügyi kalkulációra0.1 + 0.2 != 0.3
  • ==-t wrapper összehasonlításra — cache határon kívül hibás
  • float-ot, ha nem szükségesdouble a default, float ritka

Gyakorlati döntési ellenőrzőlista

Amikor típust választasz, érdemes ezt a sorrendet követni:

  1. Lehet hiányzó az érték? Ha igen, wrapper vagy dedikált domain típus is indokolt lehet.
  2. Üzletileg kritikus a pontosság? Ha igen, decimális pénzügyi értéknél inkább BigDecimal kell.
  3. Hot path-ban vagy nagy adathalmazon fut majd? Ha igen, kerüld a felesleges boxingot.
  4. API-határon halad át az érték? Legyen a típus kifejező és egyértelmű.

Tipikus példák:

  • int retryCount általában jobb, mint Integer retryCount
  • Long orderId indokolt lehet, ha a perzisztencia a “még nincs mentve” állapotot null-lal jelöli
  • BigDecimal amount jobban kifejezi az üzleti szándékot, mint double amount
  • boolean enabled tisztább, mint Boolean enabled, hacsak tényleg nincs szükség háromállapotú logikára

4. Kód példák

Alap példa — Típusok és konverzió

public class TypeBasics {
    public static void main(String[] args) {
        // Primitívek
        int count = 42;
        double price = 19.99;
        boolean active = true;
        char grade = 'A';

        // Widening (automatikus)
        long bigCount = count;          // int → long, OK
        double bigPrice = count;        // int → double, OK

        // Narrowing (explicit cast szükséges)
        int rounded = (int) price;      // 19 (csonkítás!)
        byte small = (byte) count;      // 42 (belefér)

        // Wrapper autoboxing
        Integer boxed = count;          // autoboxing
        int unboxed = boxed;            // unboxing

        // var (Java 10+) — a típust a fordító következteti ki
        var name = "Alice";             // String
        var number = 42;                // int (NEM Integer!)
        var list = List.of(1, 2, 3);    // List<Integer>
    }
}

Haladó példa — Pénzügyi kalkuláció hibája

public class MoneyPitfall {
    public static void main(String[] args) {
        // ❌ ROSSZ: double pontossági hiba
        double price = 0.1;
        double quantity = 3;
        System.out.println(price * quantity);
        // Output: 0.30000000000000004 (!)

        // ✅ HELYES: BigDecimal használata
        BigDecimal bdPrice = new BigDecimal("0.1");
        BigDecimal bdQuantity = new BigDecimal("3");
        System.out.println(bdPrice.multiply(bdQuantity));
        // Output: 0.3
    }
}

Gyakori csapda — Integer cache

public class IntegerCacheTrap {
    public static void main(String[] args) {
        // Cache-en belül (-128..127): MŰKÖDIK
        Integer a = 100;
        Integer b = 100;
        System.out.println(a == b);      // true ✅

        // Cache-en kívül: NEM MŰKÖDIK
        Integer c = 200;
        Integer d = 200;
        System.out.println(c == d);      // false ❌
        System.out.println(c.equals(d)); // true ✅

        // ⚠️ Null unboxing
        Integer nullable = null;
        // int value = nullable;  // NullPointerException!
    }
}

5. Trade-offok

Szempont Primitív Wrapper
Teljesítmény Gyors (stack, nincs allokáció) Lassabb (heap allokáció, GC)
💾 Memória Minimális (pl. int = 4 byte) Jelentős (pl. Integer = ~16 byte)
🔧 Karbantarthatóság Egyszerű, de nem lehet null Null-kezelés kell, de kifejezőbb
🔄 Rugalmasság Nem használható generics-ben Kollekcióban és generics-ben használható

Konkrét számok:

  • int[1000] → ~4 KB memória
  • Integer[1000] → ~20 KB memória (5x több!)
  • Egy millió Integer autoboxing → jelentős GC nyomás

6. Gyakori hibák

1. `==` használata wrapper-eknél

// ❌ Hibás
if (value1 == value2) { ... }  // referencia összehasonlítás!

// ✅ Helyes
if (value1.equals(value2)) { ... }
// vagy
if (Objects.equals(value1, value2)) { ... }  // null-safe

2. `double` használata pénzügyi számításokra

// ❌ Soha ne csinálj ilyet
double balance = 1000.00;
balance -= 999.99;
// balance = 0.010000000000047748 (NEM 0.01!)

// ✅ BigDecimal-t használj
BigDecimal balance = new BigDecimal("1000.00");
balance = balance.subtract(new BigDecimal("999.99"));
// balance = 0.01 ✅

3. Null unboxing

// ❌ NullPointerException dobódik
Integer count = getCountFromDb(); // null-t adhat vissza
int total = count + 1;            // NPE!

// ✅ Null check
int total = (count != null) ? count + 1 : 1;
// vagy Optional használat
int total = Optional.ofNullable(count).orElse(0) + 1;

4. Narrowing cast overflow

// ❌ Csendesen hibás eredmény
int big = 128;
byte b = (byte) big;  // b = -128 (overflow, nincs exception!)

// ✅ Ellenőrzés cast előtt
if (big >= Byte.MIN_VALUE && big <= Byte.MAX_VALUE) {
    byte b = (byte) big;
} else {
    throw new ArithmeticException("Value out of byte range");
}
// vagy Java 8+:
byte b = Math.toIntExact(big); // ArithmeticException overflow-nál

7. Mélymerülés

Mikor számít a primitív vs wrapper választás?

  • Hot path-on (pl. tight loop, valós idejű feldolgozás): mindig primitív. Autoboxing egy milliós ciklusban mérhető lassulást okoz.
  • Domain modellben: wrapper, ha az érték nullable (pl. Integer age — "nem tudjuk az életkorát" vs int age — "0 éves").
  • API tervezésnél: Optional<Integer> jobb, mint nullable Integer.

Integer cache testreszabás

A JVM -XX:AutoBoxCacheMax=<size> flag-gel bővítheti az Integer cache tartományt. Nagyfrekvenciás rendszerekben (pl. ID-k 1-10000 között) ez javíthat a teljesítményen.

`var` használat — mikor igen, mikor ne?

// ✅ Jó: a jobb oldal egyértelművé teszi a típust
var users = new ArrayList<User>();
var response = httpClient.send(request, BodyHandlers.ofString());

// ❌ Rossz: a típus nem egyértelmű
var result = process();          // mi a típusa?
var x = flag ? getA() : getB(); // melyik típus?

JIT optimalizáció

A JIT fordító sok esetben escape analysis segítségével kioptimalizálja az autoboxing-ot, ha a wrapper objektum nem "szökik ki" a metódusból. De erre nem szabad építeni — production kódban mérj!

Primitív streamek és boxing overhead

Ez a téma a Stream API-nál újra előkerül. A Stream<Integer> kényelmesnek tűnik, de sok szűrés és aggregálás mellett a boxing/unboxing költsége már mérhető lehet.

int sum = IntStream.range(0, 1_000_000)
    .filter(value -> value % 2 == 0)
    .sum();

Ha az adat valóban numerikus természetű, akkor az IntStream, LongStream és DoubleStream gyakran jobb választás, mint az objektum stream.

API-határok és a `null` szemantikája

Az int és Integer közötti döntés nem csak teljesítménykérdés, hanem szemantikai kérdés is.

  • az int quantity azt jelenti, hogy az értéknek mindig léteznie kell
  • az Integer quantity azt jelenti, hogy a hiány is reprezentálható
  • az OptionalInt belső API-kban optionalitást adhat boxing nélkül

Sok esetben ez az üzleti jelentés fontosabb, mint maga a memóriaigény.

Floating point összehasonlítás

// ❌ Soha ne használj == -t double-nél
double a = 0.1 + 0.2;
if (a == 0.3) { ... }  // false!

// ✅ Epsilon-alapú összehasonlítás
private static final double EPSILON = 1e-10;
if (Math.abs(a - 0.3) < EPSILON) { ... }  // true

8. Interjúkérdések

Miért tud `Integer` `NullPointerException`-t okozni akkor is, ha nem hívsz rajta metódust?

Mert az unboxing valójában implicit metódushívás. Ha Integer value = null; és később int x = value;, akkor a fordító lényegében value.intValue() hívást illeszt be, ami NullPointerException-t dob.

Mikor jobb a wrapper, mint a primitív egy domain modellben?

Amikor a hiányzó érték üzletileg értelmes. Például az Integer age jelentheti azt, hogy “nem ismert az életkor”, míg az int age ezt nem tudja kifejezni.

Miért fordul le ez: `short s = 1; s += 1;`, de ez nem: `s = s + 1;`?

Mert a += implicit castot végez az értékelés után. A s + 1 eredménye int, ezért a sima visszaadás short-ba szűkítő konverzió lenne, amihez explicit cast kell.

Miért ajánlott a `BigDecimal` pénzügyi számításokra a `double` helyett?

Mert a double bináris lebegőpontos formában tárol, így sok tizedes tört nem reprezentálható pontosan. A BigDecimal ezzel szemben kontrollált pontosságot és kerekítést ad, ami üzleti számításoknál kulcsfontosságú.


9. Szószedet

Kifejezés Definíció
Autoboxing Primitív típus automatikus konverziója wrapper osztállyá (intInteger)
Unboxing Wrapper osztály automatikus konverziója primitívvé (Integerint)
Widening Implicit típuskonverzió kisebb típusból nagyobba (veszteségmentes)
Narrowing Explicit típuskonverzió nagyobb típusból kisebbe (adatvesztes lehet)
Integer Cache A JVM -128 és 127 közötti Integer objektumokat cache-eli az Integer.valueOf() híváskor
Stack Memóriaterület ahol a lokális primitív változók és referenciák tárolódnak
String Pool → Lásd: String kezelés
Escape Analysis JIT technika: megállapítja, hogy egy objektum "kiszökik-e" a metódusból

10. Gyorsreferencia

  • 🔢 8 primitív típus: byte, short, int, long, float, double, char, boolean
  • 📦 Wrapper = primitív + null + generics kompatibilitás (de drágább)
  • ⚠️ == wrapper-eknél referenciát hasonlít, .equals() kell!
  • 💰 Pénzhez MINDIG BigDecimal, soha double
  • 🎯 Integer cache: -128 — 127 (bővíthető JVM flag-gel)
  • 📏 Widening automatikus, narrowing explicit cast kell
  • 🚫 Null unboxing = NPE — mindig ellenőrizd!
  • 🔍 var (Java 10+) — csak ha a típus egyértelmű a kontextusból
  • 🔐 public / private / protected — ezek mondják meg, honnan látszik egy tag
  • 🏗️ class + new — az egyik típust deklarál, a másik példányt hoz létre
  • Hot path = primitív, domain model = wrapper ha nullable kell
  • 🧮 float ≈ 7 tizedes jegy, double ≈ 15 tizedes jegy precizitás

🎮 Játékok

13 kérdés