2009-11-29

SCJP-6 03. Dvigubas return

Turbūt savaime suprantama, kad Java kompiliatorius neleistų kompiliuoti tokio kodo:

// DoubleReturnWrong.java
class DoubleReturnWrong {
  static int method() {
    return 1000;
    return 2000;
  }
  public static void main(String ... args) {
    System.out.println(method());
  }
}

Kompiliatorius aptiks, kad 5-oje eilutėje esantis return 2000; sakinys yra nepasiekiamas:

% javac DoubleReturnWrong
DoubleReturnWrong.java:5: unreachable statement
    return 2000;
    ^
1 error
Abejonių tikriausiai nekels ir šis kodas:
// DoubleReturn1000
class DoubleReturn1000 {
  static int method() {
    boolean flag = true;

    if (flag) return 1000;

    return 2000;
  }

  public static void main(String ... args) {
    System.out.println(method());
  }
}
Kadangi metodas vykdomas iki pirmojo sutikto return sakinio, ir po to priverstinai nutraukiamas, šiuo atveju method() grąžins reikšmę 1000.

Dabar -- subtilesnė situacija:

// DoubleReturn.java
class DoubleReturn {

  static int method() {
    try {
      return 1000;
    } finally {
      return 3000;
    }
}

  public static void main(String ... args) {
    System.out.println(method());
  }
}

1000 ar 3000? Pagal ankstesnį pavyzdį būtų logiška tikėtis, kad rezultatas -- 1000. T.y. grąžinama pirmo pasiekto return reiškmė. Tačiau šiuo atveju antrasis return yra finally bloke, kuris privalo būti įvykdytas bet kuriuo atveju, išskyrus System.exit() ar panašų visišką programos nutraukimą ankstesnėje try {...} arba catch(...) {...} dalyje, esančioje prieš finally bloką. Taigi, po return 1000 vykdymas pratęsiamas finally dalyje, kurioje tėra kitas return sakinys su reikšme 3000. Kaip elgsis JVM šiuo atveju? Ar šiuo atveju bus kaip su finalize() metodu (prisimenam, kad finalize gali būti JVM automatiškai įvykdytas ne daugiau kaip vieną kartą. T.y. nėra ribojama, kiek kartų jis bus iškviestas iš kodo naudojant tiesioginį kvietinį, pvz. myObject.finalize(), tačiau nepriklausomai nuo to JVM vis tiek gali vieną kartą iškviesti šį metodą prieš objekto "sunaikinimą", bet ne daugiau. Su finalize metodu yra niuansas -- metodas automatiškai iškviečiamas prieš objektą sunaikinant, tačiau šio metodo kodas gali sukurti "gyvą" nuorodą į objektą, ir taip apsaugoti jį nuo sunaikinimo. Tačiau tokiu atveju, kai objektas vėl pataps potencialiai naikinamu, JVM daugiau finalize metodo jam neiškvietinės), t.y. JVM yra "pažymėjus", kad metode jau buvo įvykdytas return sakinys?
Atsakymas -- ne. Rezultatas bus 3000. Kyla klausimas -- o kodėl? Patikrinkim "išdizassemblintą" kodą:

% javap DoubleReturn
Compiled from "DoubleReturn.java"

public class DoubleReturn extends java.lang.Object{
public DoubleReturn();
   Code:
    0: aload_0
    1: invokespecial #1; //Method java/lang/Object."":()V
    4: return

static int method();
   Code:
    0: sipush 1000
    3: istore_0
    4: sipush 3000
    7: ireturn
    8: astore_1
    9: sipush 3000
    12: ireturn

   Exception table:
    from   to  target type
     0     4     8    any
     8     9     8    any

public static void main(java.lang.String[]);
   Code:
     0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream;
     3: invokestatic #3; //Method method:()I
     6: invokevirtual #4; //Method java/io/PrintStream.println:(I)V
     9: return
}
Kol kas nesu Java bytecode specialistas, tačiau iš aukščiau pateikto listingo aiškėja seka, kuria vykdomas metodas method:
1. į steką įdedama 1000 (0-is metodo kodo adresas, sipush 1000)
2. šokama į 4-ą adresą pagal Exception Table (šioje vietoje spėju, kad taip vyksta)
3. į steką įdedama 3000 (4-as kodo adresas, sipush 3000)
4. grįžtama iš metodo su reikšme steke -- 3000 (7-ame kodo adrese esanti komanda ireturn)

Taigi kaip matome iš sukompiliuoto bytecode, return reikšmė yra talpinama steke, ir paskutinė iš jų -- grąžinama.
Egzamine neteko matyti šį "kabliuką" turinčio klausimo, bet tai nereiškia, kad negali pakliūti kam nors kitam.

2009-11-22

SCJP-6 02. Failų su klasėmis vardai ir metodas main()

Keletas mano pastebėjimų apie failų vardus bei metodą main().

Kiekvienas išeities failas (source-file) su plėtiniu .java gali turėti savyje daug deklaruotų klasių. Tačiau -- ne daugiau kaip vieną public klasę. Jos gali ir nebūti (!), bet jei public klasė apibrėžta, tuomet jos vardas turi sutapti su failo vardu (be plėtinio), kuriam klasė deklaruota. T.y. jei turime public klasę "Gyvis", ir tris kitas, package matomumo klases (pvz. "Briedis", "Begemotas" ir "Vandenynas"), tai visas šis ketvertas privalo būti patalpintas "Gyvis.java" faile. Jei public klasės nėra -- failo pavadinimas griežtų taisyklių neturi. Nepaisant to, abiem atvejais sukompiliavę išeities kodą gausime keturis atskirus .class failus (Gyvis.class, Briedis.class, Begemotas.class ir Vandenynas.class):

// Failo vardas privalo būti Gyvis.java
//Ne daugiau viena klasė gali būti deklaruota public
public abstract class Gyvis {}
class Briedis extends Gyvis {}
class Begemotas extends Gyvis{}
class Vandenynas {}
bei
// Failo vardas gali būti SCJP_1.java
abstract class Gyvis {}
class Briedis extends Gyvis {}
class Begemotas extends Gyvis{}
class Vandenynas {}

Metodas main(). Norint, kad JVM rastų šį metodą ir pradėtų programos vykdymą, jis privalo būti viešas (public), statinis (static) ir kaip parametrą naudoti eilučių masyvą (String [] args) arba, nuo Java 5, leisti perduoti nefiksuoto ilgio argumentų sąrašą (String... args).
Niuansai, kuriuos verta pastebėti:
  • nors ir statinis (t.y. "nepperrašomas" -- not overrideable) metodas gali būti apibrėžtas kaip "final". Prasmė? Neleisti paveldinčiai klasei apibrėžti  metodo tokiu pačiu vardu, ir tokiu būdu "paslėpti" esančiojo aukštesnėje klasėje (prisimenam, kad kalba eina apie statinius metodus, ne paprastus, instance metodus, todėl perrašymas -- override -- neegzistuoja). Šiuo klausimu diskusija buvo pradėjusi megztis JavaRanch forume.
  •  klasei nebūtina pačiai turėti statinį main() metodą. Užtenka, kad jis bus realizuotas kuriame nors iš "protėvių".
Apibendrinant:
// Failas KeliosKlases.java
class GrandParent {
  final public static void main(String ... args) {
    System.out.println(">>> Tai yra GrandParent klasės main(...) metodas");
  }
  class Parent extends GrandParent {}
  class MyClass extends Parent {}
}
Kompiliuojam ir vykdom MyClass:
javac
KeliosKlases.java

java MyClass
...
> Tai yra GrandParent klasės main(...) metodas

2009-11-20

SCJP-6 01. Primityvų suderinamumas

Java turi aštuonis primityviuosius duomenų tipus:

  • byte
  • short
  • int
  • longint
  • float
  • double
  • char
  • boolean

Kiti duomenų tipai (įskaitant eilutes -- String) yra klasės/masyvai/interfeisai/enum, o visų jų realizacijų (arba paprasčiau tariant, objektų) hierarchijos viršūnėje stovi Object klasė.
Primityvieji duomenų tipai skirti saugoti konkrečią skaičiaus išraišką, išskyrus boolean tipą -- jis reprezentuoja vieną iš dviejų reikšmių (true arba false). Verta pastebėti, kad nors tipiškai char duomenų tipo kintamiesiems priskiriami simboliai, iš tiesų simbolių išraiška tėra kita skaičiaus išraiška. Todėl šis kodas -- teisingas:

int intHoldingChar = 'X';
char charHoldingNumber = 123;

Kitas dalykas -- vieni duomenų tipai gali talpinti skaičius iš platesnio rėžio, kiti -- iš siauresnio. Pavyzdžiui, byte duomenų tipui saugu priskirti reikšmes nuo -128 iki 127, o short -- nuo -32768 iki 32767. Akivaizdu, kad short tipo kintamajam visada saugu priskirti byte kintamojo reikšmę, bet ne atvirkščiai (nes short tipo kintamojo reikšmė gali išeiti iš byte tipui leidžiamų reikšmių rėžio). Antru atveju Java kompiliatorius reikalauja aiškaus tipo pažymėjimo (casting), nepaisant to, kad pati reikšmė ir patenka į leidžiamą rėžį:

short s = 123;
byte b1 = (byte) s; // viskas gerai
byte b2 = s; // kompiliavimo klaida

Kada yra saugu priskirti reikšmę tiesiogiai, o kada reikalingas casting'as?

double <- float <- long <- int <- (char, short <- byte)

Sekoje rodyklėmis pažymėta saugi priskyrimo kryptis -- t.y. int reikšmę galima tiesiogiai priskirti double tipo kintamajam. Duomenų tipo char rėžis yra 0...65536, tad jo reikšmę saugiai galima priskirti tik int arba aukštesnio tipo kintamajam. Tuo tarpu char kintamajam tiesiogiai negalima priskirti jokio kito tipo kintamojo reikšmės, mat net pats mažiausias byte tipo kintamasis potencialiai gali saugoti neigiamą skaičių (o char tipo kintamieji gali saugoti tik neneigiamus skaičius).
Todėl teisingi šie kodo pavyzdžiai:

// aiškus tipo pažymėjimas nebūtinas (bet nedraudžiamas)
byte b = 123;
short s = b;
int i = s;
int i2 = b;
long lng = b;
long lng2 = i2;
double d = b;
float f = lng;

// aiškus tipo pažymėjimas privalomas
byte xb = (byte) lng;
byte xb2 = (byte) d;
int xi = (int) f;

2009-11-19

Unix komandos Windows aplinkoje

Unix, Linux, BSD sistemos "iš prigimties" turi labai naudingų "utilitų": grep, tar, diff, cat, find, ls, head, tail... "too many to mention here". Norite turėti visa tai savo Windows sistemoje? Reikėtų. Geras būdas: viską parsisiųsti iš GnuWin32 tinklapio: http://gnuwin32.sourceforge.net/.

Alternatyvus būdas: http://www.weihenstephan.de/~syring/win32/UnxUtilsDist.html. Bet man atrodo, šis paketas jau gerokai pasenęs.

Sveikintinas būdas: instaliuoti (ant realios arba virtualios mašinos) Linux/FreeBSD ar pan.

2009-11-18

SCJP-6 00. Nuo ko pradėti?

Pradžių pradžia -- ar to reikia? Priklauso nuo to, kas esat, ko norit ir ko tikitės. Jei norit įsigilinti į Javos sintaksės ir semantikos niuansus -- taip. Jei norit dirbti būdami labiau užtikrinti savo kodo teisingumu/kokybe -- taip. Jei norit pasididžiuoti prieš kitus geru SCJP rezultatu -- taip. Jei  norit įrodyti sau ir darbdaviui, kad esat žmogus, kuris nebijo iššūkių (o SCJP yra šiaip jau nemažas iššūkis) -- taip. Jei dairotės darbo už Lietuvos ribų -- taip. Jei neturit ką veikti... Gal. Jei dirbat pardavėju -- vargu.
Pradėti verta nuo susipažinimo su sertifikacijos reikalavimais. Tai leidžia suvokti, ko tikėtis tiek mokantis, tiek egzamino metu:
  • 1 dalis: apibrėžimas (declarations), inicializacija (Initialization) ir galiojimo ribos (Scoping)
  • 2 dalis: vykdymo eigos kontrolė (Flow Control)
  • 3 dalis: bazinės API žinios (API Contents)
  • 4 dalis: lygiagretus vykdymas (Concurrency)
  • 5 dalis: objektiškai orientuoto programavimo esminiai dalykai (OO Concepts)
  • 6 dalis: kolekcijų API, generics'ai (nežinau lietuviško atitikmens, gal vertėtų vadinti "bendrizmas"?) (Collections / Generics)
  • 7 dalis: pagrindai (Fundamentals)
Išlaikę egzaminą gausite lapą, kuriame surašyta kiekvienos dalies teisingai atsakytų klausimų procentas.

Dabar, kai egzamino sandara žinoma -- laikas išsiaiškinti, kaip jam ruoštis ir kur laikyti. Egzaminai laikomi Prometric egzaminavimo centruose (Vilniuje jų yra keletas, yra bent vienas ir Kaune). Ateinate į egzaminą, gaunate kelis popierinius arba plastikinius lapus užrašams, rašiklį, ir trims valandoms sėdate prie kompiuterio su specialia programa. Joje žymėsite "checkboxus", "radio" ir "tampysite" teisingus atsakymus į paliktus laisvus laukelius. Kuponą ("bilietą") egzamino laikymui iš anksto galima užsisakyti pvz. Baltijos Kompiuterų Akademijoje (akredituotame Prometric centre) arba UAB "Proact Lietuva".
O kaip egzaminui ruoštis? Skaityti tutorial'us, knygas (ypatingai egzaminui skirtą Katherine Sierra ir Bert Bates knygą), rašyti daug kodo, spręsti bandomuosius egzaminus, domėtis Java ir sveikai maitintis :)

Knygą nusipirkit arba elektroninę versiją susiraskit patys.

Programavimui reikia minimaliai paruošti savo kompiuterį. Sakote, turite Eclipse arba NetBeans? Siūlau juos naudoti darbe, bet ne ruošiantis egzaminui. Autocomplete ir Autobuild yra nuostabūs dalykai dirbant, tačiau mokantis jie tik trukdys įsisavinti kai kuriuos būtinus egzamine gerai išmanyti dalykus.

Taigi, instaliuojame:
- FAR Manager, Total Commander ar panašų "old-school" įrankį. Nebūtina, tačiau patogu, nes suteikia galimybes patogiai dirbti su archyvais (JAR -- tai yra paprasta ZIP failas su tam tikra meta informacija), turi komandinę eilutę ir paprastą bei GREITĄ failų redagavimo/peržiūros galimybę.
- prie FAR Manager -- instaliuojam 7-Zip ir Colorer priedus (plugins).
- notepad++
- Java SDK

Susitvarkome $PATH arba %PATH% ("Environment variable"), t.y. įdedam "java_pagrindinis_katalogas"/bin ir patikrinam, ar iš komandinės eilutės galim sukompiliuoti Hello World:

class HelloWorld {
    public static void main(String ... args) {
        System.out.println("Hello, Java World, I am coming!");
    }
}


Klausiate, kokiu vardu išsaugoti šį failą? Galite kad ir PirmaPrograma.java -- kadangi klasė nėra "public", reikalavimas, kad failo vardas sutaptų su klasės pavadinimu, negalioja.

Taigi, pasileidžiam Far Manager, Total Commanger (arba Command Prompt, arba Unix/Linux/Solaris konsolę -- priklausomai, kokia pas jus OS ir ką turit):

$ javac PirmaPrograma.java
$ java HelloWorld


ir patikrinam, ar konsolėje išspausdinamas reikalingas tekstas. Gera pradžia -- pusė darbo :)

2009-11-17

Sintaksės išryškinimas Blogspot sistemoje

Kadangi gaminti paveiksliukus ar rankomis keisti kodo spalvą nėra pats įdomiausias, patogiausias ir greičiausias būdas tvarkingai pateikti gabaliuką kodo Blogger tinklaraščiuose, pasinaudojau kitų autorių patarimais ir sukonfigūravau sistemą taip, kad būtų galima naudoti SyntaxHighlighter.
Štai kaip atrodo Java kodas dabar:
public class HighlightedClass {
  private String firstname;
  private String lastname;

  public HighlightedClass(String firstname, String lastname) {
    this.firstname = firstname;
    this.lastname = lastname;
  }

  public String toString() {
    return firstname + " " + lastname;
  }

  public static void main(String ... args) {
    System.out.println(new HighlightedClass("JBottle", "Blogspot"));
  }
}

2009-11-16

"welcome back"

Nuo praėjusios žiemos pabaigos buvo visokių "skubių reikalų" (magistrinio baigiamojo darbo rašymas, domėjimasis kitais aktualiais dalykais, egzaminai paralelinėse studijose, darbas...),  tad SCJP egzaminą teko kuriam laikui atidėti. Tačiau pasibaigus vasarai, entuziazmas sugrįžo ir atnešė visai neblogą rezultatą -- 96% atsakytų SCJP-6 klausimų (58 iš 60). Besiruošdamas egzaminui išsiaiškinau daug įdomių dalykų, Java kalbos subtilybių, galimybių ir ypatumų. Tad dabar bus apie ką rašyti ilgam, ir tikiuosi, šįkart entuziazmas dalintis informacija (kaip ruoštis SCJP ir ne tik) neišblės.