JAVA PAGRINDAI DETALIAU

Java final raktažodis

final raktažodis (angl. final keyword) apriboja vėlesnius pakeitimus. Kintamajam jis draudžia antrą priskyrimą, metodui – perrašymą poklasyje, o klasei – paveldėjimą.

Trys pagrindinės naudojimo vietos

Java final reikšmė skirtinguose kontekstuose
Kur rašomas?Ką apriboja?Ko nedaro?
Prie kintamojoReikšmę arba nuorodą galima priskirti tik vieną kartąNe visada padaro nurodomą objektą nekeičiamą
Prie metodoPoklasis negali metodo perrašytiNedraudžia metodo paveldėti ir iškviesti
Prie klasėsKita klasė negali jos paveldėti su extendsNedraudžia kurti klasės objektų

1. final kintamasis priskiriamas vieną kartą

Veikiantis pavyzdys

final int MINIMALUS_AMZIUS = 18;

System.out.println(MINIMALUS_AMZIUS);
Konsolės rezultatas

18

  1. Kintamajam MINIMALUS_AMZIUS pirmą kartą priskiriame 18.
  2. Šią reikšmę galima skaityti ir naudoti skaičiavimuose.
  3. final nereiškia, kad reikšmė privalo būti žinoma kompiliavimo metu – svarbu, kad ji būtų priskirta tik kartą.

Nesikompiliuojantis pavyzdys

final int MINIMALUS_AMZIUS = 18;

MINIMALUS_AMZIUS = 21; // Klaida: antras priskyrimas neleidžiamas
Rezultatas

Kodas nesukompiliuojamas. Klaidinga eilutė palikta aktyvi, o jos pabaigoje esantis komentaras paaiškina priežastį: final kintamajam jau buvo priskirta reikšmė.

Reikšmę galima suteikti konstruktoriuje

Klasės final lauką galima deklaruoti be reikšmės ir priskirti jį kiekviename konstruktoriuje. Tai vadinama tuščiuoju final lauku (angl. blank final field).

class Uzsakymas {
    final int numeris;
    String pirkejas;

    Uzsakymas(int numeris, String pirkejas) {
        this.numeris = numeris;
        this.pirkejas = pirkejas;
    }
}

Uzsakymas uzsakymas = new Uzsakymas(1042, "Ieva");
System.out.println(uzsakymas.numeris);
System.out.println(uzsakymas.pirkejas);
Konsolės rezultatas

1042
Ieva

Kiekvienas Uzsakymas objektas gali turėti kitą numerį, tačiau jau sukurto objekto numerio perrašyti nebegalima.

class Uzsakymas {
    final int numeris;

    Uzsakymas() {
        // Klaida: numeris liko be reikšmės
    }
}
Rezultatas

Kodas nesukompiliuojamas. Kiekvienas konstruktoriaus kelias privalo suteikti final laukui reikšmę tiksliai vieną kartą.

final nuoroda nereiškia nekintamo objekto

Veikiantis pavyzdys: keičiamas objekto turinys

final StringBuilder tekstas = new StringBuilder("Pirkinių sąrašas");

tekstas.append(": duona, pienas");
System.out.println(tekstas);
Konsolės rezultatas

Pirkinių sąrašas: duona, pienas

  1. final užrakina nuorodą tekstas prie vieno StringBuilder objekto.
  2. Pats StringBuilder yra keičiamas, todėl jo turinį galima papildyti su append().

Nesikompiliuojantis pavyzdys: nuoroda nukreipiama kitur

final StringBuilder tekstas = new StringBuilder("Pirkinių sąrašas");

tekstas = new StringBuilder("Kitas sąrašas"); // Klaida: antras priskyrimas
Rezultatas

Kodas nesukompiliuojamas. Keičiama ne seno objekto būsena, o pats kintamasis tekstas bandomas nukreipti į kitą objektą.

Svarbus skirtumas

final nuoroda ir nekintamas (angl. immutable) objektas nėra tas pats. Nekintamumui reikia ir tinkamo pačios klasės dizaino.

Plačiau apie keičiamus ir nekeičiamus objektus

static final – bendra klasės konstanta

class Laikas {
    static final int MINUCIU_PER_VALANDA = 60;
    static final int SEKUNDZIU_PER_MINUTE;

    static {
        SEKUNDZIU_PER_MINUTE = 60;
    }
}

int sekundziu = 2
    * Laikas.MINUCIU_PER_VALANDA
    * Laikas.SEKUNDZIU_PER_MINUTE;

System.out.println(sekundziu);
Konsolės rezultatas

7200

  1. static reiškia, kad visa klasė turi vieną bendrą reikšmę.
  2. final neleidžia jos priskirti antrą kartą.
  3. Reikšmę galima suteikti iš karto arba statiniame inicializavimo bloke.
  4. Konstantų vardai įprastai rašomi UPPER_SNAKE_CASE stiliumi.

Plačiau apie Java konstantas

final metodų parametrai ir vietiniai kintamieji

Veikiantis pavyzdys

class Pasisveikinimas {
    static String sukurti(final String vardas) {
        return "Labas, " + vardas + "!";
    }
}

System.out.println(Pasisveikinimas.sukurti("Rūta"));
Konsolės rezultatas

Labas, Rūta!

final parametrui metodo viduje negalima priskirti kitos reikšmės. Tai gali parodyti ketinimą parametro nekeisti, tačiau objekto parametro vidinė būsena ir toliau gali būti keičiama, jei jo klasė tai leidžia.

Nesikompiliuojantis pavyzdys

class Pasisveikinimas {
    static String sukurti(final String vardas) {
        vardas = "Kitas vardas"; // Klaida: final parametro keisti negalima
        return "Labas, " + vardas + "!";
    }
}
Rezultatas

Kodas nesukompiliuojamas, nes metodo viduje parametrui vardas bandoma priskirti kitą tekstą.

2. final metodo negalima perrašyti

class Mokestis {
    final double galutineKaina(double kaina) {
        return kaina * 1.21;
    }
}

class SpecialusMokestis extends Mokestis {
    double galutineKaina(double kaina) { // Klaida: final metodo perrašyti negalima
        return kaina;
    }
}
Rezultatas

Kodas nesukompiliuojamas, nes galutineKaina(double) pažymėtas final.

final metodas naudojamas tada, kai poklasiai neturi teisės pakeisti svarbios taisyklės ar algoritmo. Tačiau jie vis tiek paveldi metodą ir gali jį iškviesti:

class Mokestis {
    final double galutineKaina(double kaina) {
        return kaina * 1.21;
    }
}

class PvmMokestis extends Mokestis {
    void rodyti(double kaina) {
        System.out.println(galutineKaina(kaina));
    }
}

PvmMokestis mokestis = new PvmMokestis();
mokestis.rodyti(100);
Konsolės rezultatas

121.0

Plačiau apie Java metodų perrašymą

3. final klasės negalima paveldėti

final class AsmensKodas {
    private final String reiksme;

    AsmensKodas(String reiksme) {
        this.reiksme = reiksme;
    }

    String gauti() {
        return reiksme;
    }
}

class SpecialusKodas extends AsmensKodas { // Klaida: final klasės paveldėti negalima
    SpecialusKodas(String reiksme) {
        super(reiksme);
    }
}
Rezultatas

Kodas nesukompiliuojamas, nes SpecialusKodas bando paveldėti final klasę AsmensKodas.

final klasė užbaigia paveldėjimo grandinę. Tai naudinga, kai klasės elgesio negalima saugiai pakeisti per poklasį arba kai jos nekintamumo taisyklės priklauso nuo visiškai kontroliuojamos struktūros.

Pavyzdys iš Java

String yra final klasė. Kita klasė negali jos paveldėti ir pakeisti teksto elgsenos.

Ko final negarantuoja?

  • final nuoroda negarantuoja, kad objektas yra nekintamas.
  • final klasė savaime negarantuoja nekintamumo – jos laukai vis tiek gali būti keičiami.
  • final nepadaro kodo automatiškai saugaus kelioms gijoms.
  • final nėra prieigos modifikatorius ir nesprendžia, kas gali narį matyti.
  • final ir finally yra skirtingi Java raktažodžiai, atliekantys skirtingas užduotis.

Kada verta naudoti?

Praktinis final pasirinkimas
SituacijaSprendimas
Reikšmė ar nuoroda po priskyrimo neturi keistisfinal kintamasis
Bendra visos klasės konstantastatic final laukas
Poklasiai gali paveldėti metodą, bet negali keisti jo taisyklėsfinal metodas
Klasė neturi turėti poklasiųfinal klasė
Objektas turi būti visiškai nekintamasVien final nepakanka – reikia nekintamos klasės dizaino

Ką verta prisiminti?

  • final kintamajam leidžia tik vieną priskyrimą.
  • Tuščias final laukas turi būti inicializuotas kiekviename konstruktoriuje.
  • final nuoroda negali rodyti į kitą objektą, bet pats objektas gali likti keičiamas.
  • final metodo negalima perrašyti, tačiau galima paveldėti ir iškviesti.
  • final klasės negalima paveldėti.
  • static final dažniausiai naudojamas bendroms klasės konstantoms.