Programovací jazyk Java
3. přednáška
Obsah
Dědičnost
- Dědičnost představuje možnost přidat k základní třídě (též bázové, rodičovské, supertřídě nebo rodiči,
případně předkovi) další vlastnosti a vytvořit tak odvozenou třídu (zděděnou třídu nebo potomka či dceřinou
třídu). Odvozená třída bývá specializovanější, než základní.
- Někdy není na první pohled jasné, zda použít dědičnost (tedy oddědit potomka
od rodiče), nebo pouze využít kompozici (tzn. použít ve třídě instance jiných tříd).
- Zde je vhodné použít tzv „JE test“. Pokud se dá říci, že
nová třída JE současně původní třídou, pak je možné použít dědění. Pokud se však jako vhodnější jeví použití slova MÁ, je třeba použít kompozici.
- Následující příklad ukazuje vytvoření třídy Zamestnanec s využítím tříd Datum a Osoba. Třída Osoba umí
evidovat jméno osoby. Třída Datum umí evidovat datum. U zaměstnance potřebujeme evidovat jméno, datum narození a datum nástupu do
zaměstnání.
- JE test: Zamestnanec JE (také) Osoba, proto třídu Zamestnanec oddědíme od třídy Osoba.
Zamestnanec však MÁ nějaké datum narození a nějaké datum nástupu, proto třídu Datum využijeme formou kompozice (použijeme její
instance).
class Datum {
private int den, mesic, rok;
Datum(int den, int mesic, int rok) {
this.den = den;
this.mesic = mesic;
this.rok = rok;
}
Datum(Datum d) {
this(d.den, d.mesic, d.rok);
}
String toString() {
StringBuffer b = new StringBuffer(100);
b.append(den).append(".").append(mesic).append(".").append(rok);
return b.toString();
}
}
class Osoba {
String jmeno;
Osoba(String jmeno) {
this.jmeno = new String(jmeno);
}
String toString() {
return jmeno;
}
}
class Zamestnanec extends Osoba{
Datum narozeni, nastup;
Zamestnanec(String jmeno, Datum narozeni, Datum nastup) {
super(jmeno);
this.narozeni = new Datum(narozeni);
this.nastup = new Datum(nastup);
}
String toString() {
StringBuffer b = new StringBuffer(200);
b.append(jmeno).append(", narozen: ").append(narozeni.toString());
b.append("\nnastoupil: ").append(nastup.toString());
return b.toString();
}
}
- Jelikož je však dědičnost potenciálním zdrojem různých problémů, v poslední době je doporučováno používat místo ní spíše kompozici. Viz např.
http://www.dredwerkz.cz/java_efektivne/JE_14.html (v případě zobrazení v nesprávném kódování nutno v prohlížeči manuálně přepnout na UTF8).
Zápisy literálů – znakových hodnot a řetězců
- String kůň = "kůň";
- char start = 's';
- char znakTretiOdmocnina = '\u221B'; // proměnná obsahuje znak ∛.
- String tretiOdmocninaZ5 = "\u221B5"; // proměnná obsahuje řetězec ∛5.
Pole jako referenční datový typ
- Java obsahuje dva neprimitivní datové typy: pole a objekty. Proměnné těchto typů
jsou označovány jako referenční.
- Referenční proměnné typu pole nebo objekt po své deklaraci nereprezentují žádná data. Jak pole, tak
objekty vznikají dynamicky pomocí speciálního příkazu a zanikají, jakmile na ně neexistuje žádný odkaz.
- Poznámky:
- Pro konkrétní hodnotu referenční proměnné používáme název odkaz.
- Hodnota neplatného (neexistujícího) odkazu je hodnota konstanty null.
Deklarace pole
- Skládá se ze dvou částí: z typu pole a jména proměnné.
- Při deklaraci se neudává velikost pole. Pole jsou totiž alokována dynamicky, takže velikost pole se určuje až při žádosti o jeho vytvoření.
- Příklad deklarace referenční proměnné pole typu int:
int[] poleInt;
- Protože deklarace nepřiděluje paměť, je třeba ji před prvním použitím pole přiřadit pomocí operátoru new:
poleInt = new int[20];
- Přidělení paměti je však možné již při deklaraci:
int[] poleInt = new int[20];
Délka pole, průchod polem
Inicializované pole
- Pole nemusíme vytvářet jen pomocí new. Pokud potřebujeme mít pole naplněné určitými hodnotami, můžeme pole vytvořit pomocí
statického inicializátoru.
Příklad:
int[] prvocisla = {1, 2, 3, 5, 7, 11};
- Konstanta length v tomto případě vrací hodnotu 6, prvky pole jsou indexovány čísly 0 až 5.
- Hodnoty jednotlivých prvků pole lze dále měnit, nelze však pole rozšiřovat o další prvky.
Dvourozměrné pole
- Příklad deklarace:
int[][] a = new int[5][4];
- Počet řádků (zde 5) získáme pomocí a.length, počet sloupců (zde 4) i-tého řádku pomocí a[i].length.
- Každý řádek může mít jiný počet prvků (sloupců).
Dvourozměrné pole s různou délkou řádků
Inicializace dvourozměrného pole
- I vícerozměrné pole lze vytvořit pomocí statického inicializátoru:
int[][] b = {{ 1, 2, 3},{11, 12, 13},{21, 22, 23}};
int[][] c = {{ 1, 2, 3},{11, 12},{21}};
Troj- a vícerozměrná pole
- Vytvářejí se analogicky. V případě, že se pole vytváří postupně, nelze přeskakovat rozměry:
int[][][] d = new int[5][5][5]; // OK
int[][][] e = new int[5][5][];
e[0][1] = new int[8]; // OK
int[][][] f = new int[5][][5]; // chyba
f[0][][1] = new int[8]; // chyba
Neměnné řetězce – třída String
Vytvoření řetězce
- Při volání konstruktoru objektu String můžeme použít tyto typy skutečných parametrů: pole bytů (byte[]),
pole znaků (char[]), String, StringBuffer a StringBuilder.
- Příklad:
public class Retez1 {
public static void main(String[] args) {
String s1, s2, s3, s4, s5;
char[] znaky = {'E', 'v', 'a'};
byte[] byty = {(byte)'J', (byte)'a', (byte)'n', (byte)'a'};
StringBuffer buf = new StringBuffer("dobry den");
s1 = new String("Betalne retezec");
System.out.println("s1: " + s1); // Vypíše "s1: Betalne retezec"
s2 = new String(s1);
System.out.println("s2: " + s2); // Vypíše "s2: Betalne retezec"
s3 = new String(znaky);
System.out.println("s3: " + s3); // Vypíše "s3: Eva"
s4 = new String(bajty);
System.out.println("s4: " + s4); // Vypíše "s4: Jana"
s5 = new String(buf);
System.out.println("s5: " + s5); // Vypíše "s5: dobry den"
System.out.println("Delka s5 = " + s5.length()); // Vypise Delka s5 = 9
}
}
Inicializované pole řetězců
public class Retez2 {
public static void main(String[] args) {
String[] pole = {"Dana", "Eva", "Martina"};
for (int i = 0; i < pole.length; i++)
System.out.println(pole[i]);
}
}
Práce s řetězci String
Porovnání dvou řetězců
- int compareTo(String str) – porovná dva řetězce
- int compareToIgnoreCase(String str) – porovná dva řetězce, přičemž nerozlišuje malá a velká písmena
- Tyto dvě metody vrací hodnotu int číslo < 0, pokud je řetězec v parametru metody větší,
0 v případě shody obou řetězců a int číslo > 0, je-li menší.
- Příklad:
String s1 = new String("ahoj");
String s2 = new String("ahoi");
String s3 = new String("AHOJ");
System.out.println(s1.compareTo(s2)); // vypíše 1
System.out.println(s2.compareTo(s1)); // vypíše -1
System.out.println(s1.compareToIgnoreCase(s3)); // vypíše 0
- boolean equals(Object obj) – zjistí, zda jsou řetězce shodné
- boolean equalsIgnoreCase(String str) – totéž bez ohledu na malá a velká písmena
- Tyto metody vrací true v případě shody řetězců a false v případě neshody
Převod na malá či velká písmena
- Metody String toLowerCase() a String toUpperCase(), obě vrací String
Spojení řetězců
- Buď operátorem + nebo metodou String concat(String str)
Náhrada všech znaků v řetězci
Práce s částí řetězce
Získání části řetězce
- Znaky v řetězci jsou očíslovány od nuly.
- Metoda String substring(int beginIndex) vrací podřetězec od znaku s indexem beginIndex do konce řetězce.
- Metoda String substring(int beginIndex, int endIndex) vrací podřetězec od znaku s indexem beginIndex do znaku před
znakem s indexem endIndex.
Práce se začátkem a koncem řetězce
- Je možné snadno otestovat, jestli řetězec začíná nebo končí určitým podřetězcem.
- Metoda boolean startsWith(String prefix) vrací hodnotu true, pokud řetězec začíná podřetězcem prefix.
- Metoda boolean endsWith(String suffix) vrací hodnotu true, pokud řetězec končí podřetězcem suffix.
Oříznutí bílých znaků na okrajích
- Metoda String trim() odebere všechny případné mezery, tabulátory a znaky konce řádku na začátku i na konci řetězce.
Práce s jednotlivými znaky řetězce
Získání znaku
- Metoda char charAt(int index) vrací znak s indexem index.
Hledání znaku
- Metoda int indexOf(char c) vrací index prvního výskytu znaku c v řetězci. V případě nenalezení znaku
v řetězci vrací hodnotu -1 (to platí i pro další vyhledávací metody).
- Metoda int indexOf(char c, int fromIndex) hledá od znaku s indexem fromIndex.
- Metoda int lastIndexOf(char c) vrací index posledního výskytu znaku c v řetězci.
- Metoda int lastIndexOf(char c, int fromIndex) hledá opět odzadu, tentokrát od znaku s indexem fromIndex.
Konverze základních datových typů na řetězec
Konverze řetězce na základní datové typy
Měnitelné řetězce – třídy StringBuffer a StringBuilder
- Obě třídy slouží k práci s proměnlivými řetězci. Rozdíl mezi nimi je v tom, že práce s objekty třídy StringBuilder
je sice rychlejší, avšak tyto objekty nejsou synchronizované (tzn. nevhodné pro současný přístup z několika vláken).
- Obě třídy mají stejné metody. K modifikaci uloženého řetězce slouží metody append, insert a replace.
- Více viz dokumentace Java API: StringBuffer,
StringBuilder.
Proměnné z pohledu místa deklarace
- V Javě neexistují žádné „globální“ proměnné. Každá proměnná někomu patří, buď třídě (statická proměnná) nebo instanci.
- Proměnné třídy deklarujeme ve třídě (mimo těla metod) s klíčovým slovem static.
- Proměnné instance deklarujeme rovněž ve třídě (mimo těla metod).
- Třetí možností je deklarace proměnné přímo v těle metody. Taková proměnná je viditelná pouze v této metodě. Pokud je deklarována
v bloku, je její viditelnost omezena pouze na tento blok.
- Proměnné tedy dělíme na
- proměnné třídy (= statické),
- proměnné instance a
- lokální proměnné (metod nebo jejich podčástí – bloků).
- Příklad:
public class Prom {
static int i = 5;
int j = 8;
public static void tisk () {
System.out.println(i);
System.out.println(j); // Chyba při kompilaci: Z metody třídy nelze přímo přistupovat k proměnné instance.
}
public static void main (String[] args) {
char i = 'a';
tisk(); // vytiskne 5
System.out.println(i + " " + Prom.i); // Vytiskne "a 5". Lokální proměnná i překryla proměnnou třídy stejného názvu, proto je k proměnné třídy nutno přistupovat přes název třídy.
for(int k = 0; k < 10; k++) {
System.out.println(k);
...
}
System.out.println(k); // Chyba při kompilaci: Platnost proměnné k je omezena pouze na příkaz for.
}
}
Modifikátory přístupnosti třídy
- Před klíčovým slovem class může být uveden identifikátor public. Ten označuje veřejnou třídu, která je
přístupná i mimo svůj balík. Bez tohoto modifikátoru je třída přístupná pouze ve svém balíku.
- Veřejná třída musí být uložena v souboru stejného názvu (např. public class Student {...} v souboru Student.java)
Modifikátory přístupnosti proměnných a metod
- Java definuje čtyři stupně přístupu k metodám, které se nastavují pomocí modifikátorů v hlavičce metody:
- public – přístupné odkudkoliv,
- protected – přístupné v rámci balíčku a potomků třídy (ty mohou být v jiných balíčcích),
- implicitní (bez modifikátoru) – přístupné v rámci balíčku,
- private – viditelné pouze v rámci třídy.
- Podrobněji ZDE.
- V zájmu bezpečnosti a zapozdřenosti je vhodné postupovat od nejvyššího zapouzdření (private) a v případě potřeby „zviditelňovat“. Opačně totiž prakticky postupovat nelze (např. zapouzdřit již zveřejněnou metodu).
- Veřejné proměnné by se neněly používat prakticky vůbec, výjimku tvoří veřejné konstanty.
Výjimky
- Mechanismus výjimek byl zaveden kvůli bezpečnosti programů.
- Nutí programátora předem reagovat na potenciální problémy (výjimečné stavy) v běhu programu (neotevřený soubor, málo paměti, ...).
- Pokud v programu použijeme operace, které by mohly způsobit výjimku (např. metoda obyčejného čtení ze standardního vstupu System.in.read()), kompilátor nepřeloží program, dokud potenciální chybový stav (např. když na vstupu nic nebude) neošetříme.
Možné druhy výjimek
- Java rozlišuje tři základní druhy výjimek: Error, RuntimeException a Exception. Programátora však nutí k ošetření pouze poslední uvedené výjimky (Exception), ošetření prvních dvou je dobrovolné.
- Všechny výjimky jsou potomky jedné třídy, třídy Throwable – „vyhoditelná“.
- Třídy Error a Exception jsou potomky třídy Throwable, třída RuntimeException je potomkem třídy Exception.
- S třídou Throwable se přímo nepracuje, poskytuje totiž velmi obecnou informaci, že nastala nějaká chyba.
- Třída Error reprezentuje chyby virtuálního stroje (JVM), např. nedostatek paměti – OutOfMemoryError nebo přetečení zásobníku – StackOverflowError. V programu se obvykle neošetřují (některé ani nelze ošetřit).
- Třída Exception a její potomci, mimo větev RuntimeException, jsou výjimky, u kterých překladač kontroluje, zda je ošetřujeme. Mezi typické zástupce patří chyby vzniklé při práci se vstupy a výstupy. Na možnost
výskytu výjimky musíme v kódu nějak reagovat (viz dále), jinak překladač vypíše upozornění a kód nepřeloží.
- Třída RuntimeException a její potomci reprezentují chyby, na které lze také úspěšně reagovat, ale u kterých není překladačem vyžadováno jejich ošetření. Jsou to například ArithmeticException, ArrayOutOfBoundsException,
NullPointerException nebo NumberFormatException. V mnoha případech může programátor předejít těmto výjimkám testováním aplikace nebo pečlivostí při programování.
- Vždy je ale dobré vědět, jaké výjimky může volané metoda způsobit (najdeme v dokumentaci příslušné třídy).
Třída Throwable
- Má mj. tyto konstruktory:
- Throwable()
- Throwable(String message) – parametr message je zpráva, kterou po výskytu výjimky vrací metoda getMessage().
- Throwable(String message, Throwable cause) – parametr cause označuje příčinu výjimky (může být null).
- Throwable(Throwable cause)
- Má mj. tyto metody:
- String getMessage() – vrací textový popis výjimky.
- StackTraceElement[] getStackTrace() – metoda vrací pole záznamů o jednotlivých metodách na systémovém zásobníku v okamžiku, kdy došlo k výjimce.
- void printStackTrace() – metoda vypisuje na std. výstup pořadí volání metod na systémovém zásobníku v okamžiku, kdy došlo k výjimce. Tento výstup může významně pomoci při odstraňování chyb v aplikaci.
Způsoby ošetření výjimky
- Programátor má tři možnosti, jak reagovat na výjimku:
- neošetřovat ji, pouze o ní podat informaci volající metodě,
- výjimku zachytit a komplexně ošetřit,
- výjimku částečně nebo komplexně ošetřit a současně předat informaci volající metodě.
Předání výjimky volající metodě
Kompletní ošetření výjimky
- K této činnosti používáme jazykovou konstrukci try-catch(-finally)
- Do bloku try uzavřeme celý kód, ve kterém se může výjimka vyskytnout. Jedná se o tzv. chráněný blok.
- Blok catch, který za blokem try bezprostředně následuje, říká, na jakou výjimku se bude reagovat a jak.
Těchto bloků může následovat i několik za sebou, každý může zachytávat jinou výjimku. V případě více výjimek (více bloků catch), jsou-li výjimky ve vztahu dědičnosti, je třeba v programu umístit dříve (výše) zachytávání speciálnější výjimky (podtřídy).
- Nepovinný koncový blok finally slouží k zápisu příkazů, které se mají provést, ať už výjimka nastane či nikoliv (např. vyprázdnění
vyrovnávací paměti a uzavření souboru, do kterého jsme zapisovali).
- Syntaxe:
try {
// hlídaný blok
}
catch(třídaVýjimek1 referenceNaVýjimku1) {
// ošetření výjimky 1
}
catch(třídaVýjimek2 referenceNaVýjimku1) {
// ošetření výjimky 2
}
finally {
// zde uzavřený kód se provede vždy
}
- Příklad:
import java.io.IOException;
public class OsetreniVyjimky {
private int pocetCihel;
public int nactiCisloInt() {
byte[] pole = new byte[20];
try {
System.in.read(pole);
String nacteno = new String(pole).trim();
return Integer.valueOf(nacteno);
}
catch (IOException e) {
System.out.println("Chyba pri cteni ze std. vstupu. Nastala vyjimka " + e.toString());
e.printStackTrace();
return -1;
}
catch (NumberFormatException e) {
System.out.println("Chyba pri prevodu retezce na cislo. Nastala vyjimka " + e.toString());
StackTraceElement[] zasobnik = e.getStackTrace();
for (int i = 0; i < zasobnik.length; i++) {
System.out.print("Třída " + zasobnik[i].getClassName());
System.out.print(" uložená v souboru " + zasobnik[i].getFileName());
System.out.print(", metoda " + zasobnik[i].getMethodName());
System.out.println(", řádek " + zasobnik[i].getLineNumber() + ".");
}
return -1;
}
}
public void nactiVstupy() {
System.out.print("Zadej pocet cihel: ");
pocetCihel = nactiCisloInt();
}
}
- Při problému se čtením ze vstupu bude vytvořena a zachycena výjimka IOException a bude vypsáno:
Chyba pri cteni ze std. vstupu. Nastala vyjimka java.io.IOException
java.io.IOException
-1
at OsetreniVyjimky.nactiCisloInt(OsetreniVyjimky.java:16)
at OsetreniVyjimky.main(OsetreniVyjimky.java:48)
- Při problému s převodem řetězce na číslo (např. při zadání vstupu "abcd") bude vytvořena a zachycena výjimka NumberFormatException a bude vypsáno:
Chyba pri prevodu retezce na cislo. Nastala vyjimka java.lang.NumberFormatException: For input string: "abcd"
Třída java.lang.NumberFormatException uložená v souboru NumberFormatException.java, metoda forInputString, řádek 65.
Třída java.lang.Integer uložená v souboru Integer.java, metoda parseInt, řádek 492.
Třída java.lang.Integer uložená v souboru Integer.java, metoda valueOf, řádek 582.
Třída OsetreniVyjimky uložená v souboru OsetreniVyjimky.java, metoda nactiCisloInt, řádek 16.
Třída OsetreniVyjimky uložená v souboru OsetreniVyjimky.java, metoda main, řádek 47.
-1
Vyvolání výjimky
-
Na výjimku je možné pohlížet jako na další datový typ Javy, který vrátí volaná metoda. Aby metoda mohla vrátit výjimku, musí metoda vytvořit odvozený objekt
z třídy Exception a vyhodit takto vzniklou výjimku pomocí klíčového slova throw.
-
Příklad vytvoření objektu výjimky IOException:
void metoda() throws IOException {
// tělo metody
if (vyskytla_se_chyba)
throw new IOException();
}
- Příklad – při zadání čísla <= 0 bude vyhozena výjimka:
public int nactiCisloInt() {
byte[] pole = new byte[20];
try {
System.in.read(pole);
String nacteno = new String(pole).trim();
int cisloInt = Integer.valueOf(nacteno);
if (cisloInt <= 0) {
throw new IllegalArgumentException("Počet cihel musí být kladný.");
}
return cisloInt;
} catch (IOException e) {
System.out.println("Chyba pri cteni ze std. vstupu. Nastala vyjimka " + e.toString());
e.printStackTrace();
return 0;
} catch (NumberFormatException e) {
System.out.println("Chyba pri prevodu retezce na cislo. Nastala vyjimka " + e.toString());
StackTraceElement[] zasobnik = e.getStackTrace();
for (int i = 0; i < zasobnik.length;
i++) {
System.out.print("Třída " + zasobnik[i].getClassName());
System.out.print(" uložená v souboru " + zasobnik[i].getFileName());
System.out.print(", metoda " + zasobnik[i].getMethodName());
System.out.println(", řádek " + zasobnik[i].getLineNumber() + ".");
}
return 0;
} catch (IllegalArgumentException e) {
System.out.println("Chybně zadaný vstup. Nastala vyjimka " + e.toString());
e.printStackTrace();
return 0;
}
}
Výpis při zadání vstupu 0:
Zadej pocet cihel: 0
Chybně zadaný vstup. Nastala vyjimka java.lang.IllegalArgumentException: Počet cihel musí být kladný.
java.lang.IllegalArgumentException: Počet cihel musí být kladný.
at pokusy.OsetreniVyjimky.nactiCisloInt(OsetreniVyjimky.java:27)
at pokusy.OsetreniVyjimky.nactiVstupy(OsetreniVyjimky.java:54)
-
Pokud pomocí několika bloků catch zachytáváme více výjimek a mezi některými z nich je vztah podtřída–nadtřída, musíme je v blocích catch uvést ve směru od podtřídy k nadtřídě. Ve výše uvedeném příkladu je třída
NumberFormatException podtřídou třídy IllegalArgumentException a proto musí být zachycována dříve. Pokud bychom pořadí zaměnili, zachytával by blok catch (IllegalArgumentException e) i výjimku
podtřídy NumberFormatException. Kompilátor by to nepřeložit s odůvodněním error: exception NumberFormatException has already been caught.
Vytvoření vlastní výjimky
Přehled častých výjimek
Výjimka |
Popis |
IllegalArgumentException |
Používá se v situaci, kdy byla metoda zavolána se „špatným“ parametrem. Použijeme ji například při vytváření čtverce – při zadání záporné délky strany. |
NullPointerException |
Vzniká v situaci, kdy identifikátor neobsahuje odkaz na instanci, ale konstantu null. Jedná se například o situaci, kdy je volána metoda instance, ale proměnná, která by měla obsahovat adresu objektu, obsahuje hodnotu null. |
IllegalStateException |
Používá se v situaci, kdy je zavolána přípustná metoda, ale instance je ve stavu, kdy metodu nelze provést. Např. pokud se po zavření souboru objeví požadavek na čtení z tohoto souboru, vznikne tato výjimka. |
NumberFormatException |
Vzniká při převodu řetězce na číslo, když vstupní řetězec nelze na číslo převést. Např. při převodu následujících řetězců na int: "45gt" nebo "4.8". |
ArrayIndexOutOfBoundException |
Zadaný index je mimo rozsah pole. |
ClassCastException |
Chyba při přetypování instancí. Výjimka je vyhozena při pokusu o přetypování na nekompatibilní typ. Např. Object x = new Integer(0); String s = (String)x; |
FileNotFoundException |
Soubor neexistuje či nelze vytvořit. |
IOException |
Chyba vstupu/výstupu, předek více podrobných výjimek, např. FileNotFoundException. |
Auto boxing/unboxing primitivních typů
- Přítomnost primitivních typů v jinak objektové Javě vede k potřebě jejich objektových reprezentací. Těmi jsou například
pro typ int třída Integer, pro boolean třída Boolean atd. Pro různé operace s nimi jsou někdy potřeba
jejich primitivní formy a jindy zase objektové reprezentace.
- Dříve bylo nutno převádět objektové typy na primitivní (tzv. unboxing) pomocí metody typValue(), kde místo
slova typ použijeme název odpovídajícího datového typu. Pro opačný převod – z primitivního na objektový typ
(tzv. boxing) – bylo třeba využít konstruktoru příslušné třídy. Příklad:
Integer iObj = new Integer(8);
int i = iObj.intValue();
- Od verze 5.0 je v jazyku Java implementován tzv. auto-boxing a auto-unboxing, takže je možné primitivní a objektové číselné typy
libovolně zaměňovat. Příklad:
Integer iObj = 8;
int i = iObj;
The End