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.
intmindig 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
booleanmérete implementáció-függő: a JVM általábanint-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
intliterálok alapértelmezetteninttípusúak, a tizedes literálokdoubletí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ésdoubleközötti konverzió precíziós veszteséggel járhatint→floatkonverzió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értelmezetteninttípusúak - A
longliterálhozLutótag szükséges:42L - A lebegőpontos literálok, például a
3.14, alapbóldoubletípusúak - A
floatliterálhozfutó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
booleandefault értékefalse
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 gyakranpublic. - A
newlétrehozza az objektumot, a konstruktor pedig inicializálja. - A
staticés az instance világát ne keverd: static kontextusban nincsthis. - A
finalnem 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óra —0.1 + 0.2 != 0.3 - ❌
==-t wrapper összehasonlításra — cache határon kívül hibás - ❌
float-ot, ha nem szükséges —doublea default,floatritka
Gyakorlati döntési ellenőrzőlista
Amikor típust választasz, érdemes ezt a sorrendet követni:
- Lehet hiányzó az érték? Ha igen, wrapper vagy dedikált domain típus is indokolt lehet.
- Üzletileg kritikus a pontosság? Ha igen, decimális pénzügyi értéknél inkább
BigDecimalkell. - Hot path-ban vagy nagy adathalmazon fut majd? Ha igen, kerüld a felesleges boxingot.
- 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, mintInteger retryCountLong orderIdindokolt lehet, ha a perzisztencia a “még nincs mentve” állapototnull-lal jelöliBigDecimal amountjobban kifejezi az üzleti szándékot, mintdouble amountboolean enabledtisztább, mintBoolean 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óriaInteger[1000]→ ~20 KB memória (5x több!)- Egy millió
Integerautoboxing → 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" vsint age— "0 éves"). - API tervezésnél:
Optional<Integer>jobb, mint nullableInteger.
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 quantityazt jelenti, hogy az értéknek mindig léteznie kell - az
Integer quantityazt jelenti, hogy a hiány is reprezentálható - az
OptionalIntbelső 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á (int → Integer) |
| Unboxing | Wrapper osztály automatikus konverziója primitívvé (Integer → int) |
| 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, sohadouble - 🎯 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