The wiki page is under active construction, expect bugs.

This is an old revision of the document!


Programování v jazyce JAVA: vlastnosti a koncepce jazyka. Principy objektového programování.

B0B36PJV Webové stránky předmětu

  • Vývojové prostředí – JDK, JVM, kompilace a běh programu, správa paměti, GC, profilování a optimalizace.
  • Objekty, třídy a jejich vztahy – princip abstrakce a zapouzdření, modifikátory přístupu. Interface a abstraktní třída. Dědičnost a kompozice, polymorfismus, dynamická vazba.
  • Výčtové typy – práce s kolekcemi, vzor iterátor, generické typy.
  • Vnitřní a anonymní třídy – imutabilita, vzor singleton. Proměnné a metody třídy vs. instance.
  • Mechanismus výjimek – typy a jejich ošetření, vlastní výjimky. Práce se soubory – přístup k souboru, textové vs. binární, proudy, ukládání dat. Sokety – typy soketů, typy spojení, síťová komunikace.
  • Paralelismus – vícevláknové aplikace, problém souběhu a zastavení. Tvorba vláken a jejich ukončení, threadpool, synchronizace, volatilita.

Vývojové prostředí – JDK, JVM, kompilace a běh programu, správa paměti, GC, profilování a optimalizace

JDK, JRE, JVM

Většinu programovacích jazyků je možné rozdělit do dvou kategorií:

  • kompilované - program je zkompilovaný do spustitelného souboru, skládá se z nativních procesorových instrukcí, které přímo vykonává procesor a musí být tedy kompilován pro konkrétní architekturu a systém. Mezi tyto jazyky patří například C/C++.
  • interpretované - program není zkompilovaný, pro spuštění je nutný jiný program (interpreter), který program při spuštění převádí do nativních procesorových instrukcí, které může počítač vykonávat. Typický interpretovaný jazyk je třeba Python.

Javu je možné zařadit do obou kategorií - program se první zkompiluje do bytecode, který je obecný - není specifický pro danou platformu. Pro tento proces je potřeba JDK (java development kit).

Tento bytecode je možné následně pustit s JRE (java runtime enviroment - subset JDK), který bytecode při spuštění interpretuje.

Java tedy umožňuje program zkompilovat pouze jednou do bytecode a následně spustit na jekékoliv platformě s JRE (i když v praxi to obecně tak jednoduché není - compile once, debug everywhere). Tento přístup vede k rychlosti blížící se kompilovaným jazykům bez nutnosti kompilovat pro každou platformu zvlášť.

JVM (java virtual machine) je obsažen v JRE i JDK a je zodpovědný za překlad bytecode do nativních procesorových instrukcí.

Správá paměti

Stack

Malý paměťový prostor, ve kterém se typicky ukládají lokální promněné (což obsahuje i reference, atd). Funguje na principu last-in first-out. Při zavolání funkce se na stacku alokuje místo pro všechny lokální promněné dané funkce. Implementováno přes stack-pointer, který ukládá adresu konce stacku.

Heap

Velký paměťový prostor, dynamicky alokovaná paměť. Typicky se zde ukládají objekty a větší datové struktury. Alokována pomocí new. O dealokaci se nestará programátor, ale Garbage Collector (GC).

GC

Garbage Collector běží na pozadí JVM. Běží na vlastním vlákně (nebo i vláknech), takže neblokuje běh samotného programu (asynchronní). Drží si seznam všech referencí na data na heap a jakmile zjistí, že už žádné reference neexistují, tak data uvolní (dealokuje).

Garbage Collector sice nebude nikdy tak efektivní jako korektní manuální dealokace jako např. v C/C++, ale zjednodušuje psaní programu, jeho udržitelnost a značně snižuje riziko memory leaků kvůli špatným manuálním dealokacím.

Profilování a optimalizace

JDK a z části i JRE implementuje několik nástrojů pro profiling programu, jako třeba vytížení procesoru a jednotlivých vláken, využití paměti, stav GC.

JDK provádí mnoho optimalizací již při kompilaci bytecode, jako např. eliminace nepoužitého kodu, inlining, atd.. Následně probíhá i několik optimalizací při interpretaci programu, jako např. cachování kompilace do nativních procesorových instrukcí.

Objekty, třídy a jejich vztahy

Třída je šablona pro vytváření tříd, objekt je konkrétní instance třídy.

Například:

// class definition
public class Person {
    private String name;
    private int age;
 
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
 
    public int getAge() {
        return age;
    }
}
 
// object instantiation
Person person = new Person("John", 30);

Vztahy mezi objekty:

  • Abstrakce - koncepty organizujeme do tříd, objekty jsou instancemi tříd
  • Zapouzdření - objekty mají svůj stav skrytý, komunikujeme s nimi přes jejich rozhraní, nezajímá nás jejich vniřní implementace
  • Dědičnost - umíme vytvořit hierarchii tříd se společnými vlastnostmi, které se dále specializují
  • Polymorfismus - objekt se stejným rozhraním může zastoupit jiný se stejným rozhraním

Výčtové typy

Výčtový typ / enumerate, je typ který může nabývat jenom předem stanovených hodnot. Hodí se například pro uchovávání nějakého stavu, místo arbitrárně zvolených čísel.

Například:

public enum Day {
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}
 
Day today = Day.MONDAY;
 
public enum Suit {
    CLUBS(Color.BLACK),
    DIAMONDS(Color.RED),
    HEARTS(Color.BLACK),
    SPADES(Color.RED);
 
    private Color color;
 
    Suit(Color c) {
        this.color = c;
    }
 
    public Color getColor() {
        return color;
    }
 
    public boolean isRed() {
        return color == Color.RED;
    }
}

Vnitřní a anonymní třídy

Třída která je definovaná uvnitř jiné třídy.

class OuterClass {
  int x = 10;
 
  class InnerClass {
    int y = 5;
  }
}

Anonymní třída je třída vnitřní bez názvu, pro kterou je vytvořen pouze jediný objekt.

GeeksforGeeks

// Interface
interface Age {
    int x = 21;
    void getAge();
}
 
class AnonymousDemo {
 
    // Main driver method
    public static void main(String[] args)
    {
 
        // A hidden inner class of Age interface is created
        // whose name is not written but an object to it
        // is created.
        Age oj1 = new Age() {
 
            @Override public void getAge()
            {
                // printing  age
                System.out.print("Age is " + x);
            }
        };
 
        oj1.getAge();
    }
}

Mechanismus výjimek

Výjimka (exception) je neočekávaná událost během běhu programu, kvůli níž nelze pokračovat v běžném průběhu. Typickým příkladem je dělení nulou nebo přístup mimo rozsah pole.

Hierarchie

  • Vše začíná třídou Throwable.
    • Error – vážné chyby JVM (např. OutOfMemoryError), většinou je *nechytáme.
    • Exception – vše, co smysluplně ošetřujeme.

Checked × Unchecked

  • Checked – kompilátor hlídá; musíš je buď zachytit pomocí catch, nebo předat dál klíčovým slovem throws (např. FileNotFoundException).
  • Unchecked – všechny výjimky odvozené od RuntimeException a také Error; kompilátor jejich ošetření nevynucuje (např. NullPointerException).

Ošetření („try-catch-finally“)

try {
    // rizikový kód
} catch (IOException | SQLException ex) {  // multi-catch od Java 7
    log(ex);
    throw ex;         // volitelné předání dál
} finally {
    cleanup();        // provede se vždy
}
  • try-with-resources (Java 7+) – vše, co implementuje AutoCloseable, se zavře automaticky:
try (BufferedReader br = Files.newBufferedReader(Path.of("data.txt"))) {
    return br.readLine();
}

Vlastní výjimky

public class DataFormatException extends Exception {           // checked
    public DataFormatException() {}
    public DataFormatException(String msg)       { super(msg); }
    public DataFormatException(String msg, Throwable cause) {
        super(msg, cause);
    }
}
  • Kdy se hodí? Když stávající výjimky nedostačují a potřebuješ popsat specifickou situaci.
  • Rozhodni se, zda má být checked (nutí volající k ošetření) nebo unchecked (dědí z RuntimeException).

Best practices

  • Vyhazuj co nejkonkrétnější výjimky, ať je volající může smysluplně řešit.
  • Nezachytávej zbytečně obecné Exception/Throwable – skrýváš tím chyby.
  • Vždy uvolňuj prostředky (soubor, socket…) v finally nebo ještě lépe v try-with-resources.
  • Přidávej smysluplné chybové zprávy, aby bylo z logu jasné *proč* se výjimka stala.

Práce se soubory (java.io)

Klíčové třídy

  • File – abstraktní reprezentace cesty; umí zjišťovat existenci, práva, velikost a vytvářet adresáře/základní soubory.
  • InputStream / OutputStream – bajtové proudy (binární data).
    • Dekorátory: Buffered* (buffer), Data* (primitiva), Object* (serializace), GZIP* (komprese)…
  • Reader / Writer – znakové proudy (text, kódování).
    • Typicky BufferedReader, InputStreamReader (převod bajt→znak), FileWriter atd.
  • RandomAccessFile – čtení/zápis s libovolným posuvem uvnitř souboru.
  • Rozhraní Closeable / Flushable / Serializable – sjednocují zavírání, flush a serializaci.

Vytvoření a použití proudů

try (InputStream  in  = new FileInputStream("logo.png");
     OutputStream out = new FileOutputStream("copy.png")) {
 
    byte[] buf = new byte[8192];
    int n;
    while ((n = in.read(buf)) != -1) {
        out.write(buf, 0, n);
    }
}   // oba proudy se zavřou automaticky (try-with-resources)

Otevření jednoduchého textového souboru (java.io)

Nejčastější (a dodnes plně dostačující) kombinací je dvojice ``FileReader`` + ``BufferedReader``:

try (BufferedReader br =
         new BufferedReader(new FileReader("soubor.txt"))) {
 
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
} // try-with-resources zajistí zavření souboru
  • ``FileReader`` čte znaky (automaticky použije defaultní kódování platformy, nebo explicitně zadej např. ``StandardCharsets.UTF_8``). :contentReference[oaicite:0]{index=0}
  • ``BufferedReader`` obalí čtení do větších bloků → méně systémových volání, vyšší výkon. :contentReference[oaicite:1]{index=1}
  • Konstrukce try-with-resources (Java 7+) zavře reader i v případě výjimky. :contentReference[oaicite:2]{index=2}

Textové vs. binární

  • TextReader/Writer, nutné správné kódování (UTF-8 od JDK 18 default).
  • BinárníInputStream/OutputStream; rychlejší, ale nečitelné pro člověka.

Serializace objektů

try (ObjectOutputStream oos =
         new ObjectOutputStream(new FileOutputStream("state.bin"))) {
    oos.writeObject(gameState);
}
  • Třída musí implementovat Serializable.
  • Pozor na kompatibilitu verzí tříd (pole serialVersionUID).

Best practices

  • Vždy používej try-with-resources – žádné zapomenuté close().
  • Bufruj I/O (Buffered*) kvůli výkonu.
  • Nezachytávej holou Exception; loguj a přeposílej konkrétní typy.
  • Při sériové práci se soubory preferuj nové NIO API (java.nio.file.Path) – ošetří limity File a umí asynchronní I/O, ale porozumění java.io je nutný základ.

Sokety

Typy soketů a API

Třída Protokol Povaha Typická použití
Socket / ServerSocket TCP spojované, spolehlivé chat, přenos souborů
DatagramSocket UDP nespojované, best-effort streaming, hry
MulticastSocket UDP multicast skupinový přenos discovery, multicast video

TCP vs. UDP – typy spojení

  • TCP – potvrzuje doručení a pořadí, detekuje ztráty; pomalejší, ale jistý.
  • UDP – žádné záruky, ale nízká latence; sám si případně řešíš potvrzení či opakování paketů.

Ukázkový TCP server/klient

// === Server ===
try (ServerSocket srv = new ServerSocket(9000)) {
    while (true) {
        try (Socket s = srv.accept();
             BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
             PrintWriter    out = new PrintWriter(s.getOutputStream(), true)) {
 
            String line = in.readLine();
            out.println("Echo: " + line);
        }
    }
}
 
// === Klient ===
try (Socket s = new Socket("localhost", 9000);
     BufferedReader in  = new BufferedReader(new InputStreamReader(s.getInputStream()));
     PrintWriter    out = new PrintWriter(s.getOutputStream(), true)) {
 
    out.println("Ahoj světe");
    System.out.println(in.readLine());
}

Best practices

  • Porty a backlog: při new ServerSocket(port, backlog) nastavíš frontu nepřijatých spojení.
  • Nastav setSoTimeout() → ochrana proti zablokování čtení.
  • Na mnoho paralelních spojení použij java.nio.channels + selektory (non-blocking I/O).
  • Pro šifrovaný přenos sáhni po SSLSocket / SSLServerSocket nebo moderním klientovi java.net.http.
  • Vždy uzavírej soket v try-with-resources nebo finally.

Paralelismus

Nutné dodržet pravidla synchronizace, aby se zabránilo deadlocku.

Objekty jsou odvozené od třídy Thread, tělo nezávislého výpočtu definujeme v metodě run().

public class Worker extends Thread {
    private final int numberOfJobs;
 
    public Worker(int id, int jobs) {
        super("Worker " + id);
        myID = id;
        numberOfJobs = jobs;
        stop = false;
        System.out.println("Worker id: " + id + " has been created threadID:" + getId());
    }
 
    public void run() {
        doWork();
    }
}
 
Worker thread = new Worker(1, 10);
thread.start(); //new thread is created
System.out.println("Program continues here");

Pokud nelze použít dědění od Thread implementujeme rozhraní Runnable:

public class WorkerRunnable implements Runnable {
    private final int id;
    private final int numberOfJobs;
 
    public WorkerRunnable(int id, int jobs) {
        this.id = id;
        numberOfJobs = jobs;
    }
 
    public String getName() {
        return "WorkerRunnable " + id;
    }
 
    @Override
    public void run() { ... }
}
 
WorkerRunnable worker = new WorkerRunnable(1, 10);
Thread thread = new Thread(worker, worker.getName());
thread.start();
 
public void run() {
    Thread thread = Thread.currentThread();
 
    for (int i = 0; i < numberOfJobs; ++i) {
        System.out.println("Thread name: " + thread.getName());
    }
} 
Navigation

Playground

QR Code
QR Code statnice:bakalar:b0b36pjv (generated for current page)