Deprecated: Function set_magic_quotes_runtime() is deprecated in /DISK2/WWW/lokiware.info/mff/wakka.php on line 35
Deprecated: Function split() is deprecated in /DISK2/WWW/lokiware.info/mff/formatters/classes/WackoFormatter.php on line 256
Deprecated: Function split() is deprecated in /DISK2/WWW/lokiware.info/mff/formatters/classes/WackoFormatter.php on line 256
Deprecated: Function split() is deprecated in /DISK2/WWW/lokiware.info/mff/formatters/classes/WackoFormatter.php on line 256
Deprecated: Function split() is deprecated in /DISK2/WWW/lokiware.info/mff/formatters/classes/WackoFormatter.php on line 256
Deprecated: Function split() is deprecated in /DISK2/WWW/lokiware.info/mff/formatters/classes/WackoFormatter.php on line 256
Deprecated: Function split() is deprecated in /DISK2/WWW/lokiware.info/mff/formatters/classes/WackoFormatter.php on line 256
Deprecated: Function split() is deprecated in /DISK2/WWW/lokiware.info/mff/formatters/classes/WackoFormatter.php on line 256
Deprecated: Function split() is deprecated in /DISK2/WWW/lokiware.info/mff/formatters/classes/WackoFormatter.php on line 256
Deprecated: Function split() is deprecated in /DISK2/WWW/lokiware.info/mff/formatters/classes/WackoFormatter.php on line 256
Java
Přednášející: Petr Hnětynka (Na jeho webu najdete slajdy, požadavky k zápočtu apod.)
Zápočet
K zápočtu je třeba docházka a/nebo úkoly, zápočtový program (projekt) a praktický zápočtový test v labu (podrobnosti na
Hnětynkových stránkách). Za zmínku stojí, že s sebou můžete mít jakékoli psané a tištěné materiály, ale na webu můžete jen na dokumentaci JDK a, podle toho co říkal, i když na webu to nemá, slajdy z přednášky.
Možné řešení testu ze 4. 2. 2009 14:00: file:zapoctovadb.java — Adam
Pozorování: Některá zadání jsou výrazně lehčí, a některá výrazně těžší. Na nic nepotřebujete být geniální programátoři ani mít hluboké znalosti API a už vůbec ne objektově orientovaného programování (Mimochodem, všimli jste si, že tohle je společný znak všech zkoušek a zápočtů z objektově orientovaných jazyků a dokonce i předmětu, který se z mě neznámého důvodu jmenuje Objektově orientované programování? Není to podezřelé?), ale některé věci prostě sežerou víc kódu, a tedy i času. Časový limit bývá docela přesně dodržován (tj. prodlužuje se třeba o čtvrt hodiny, ale ne o hodinu). Úspěšnost některých termínu je proto menší než poloviční. Takže pokud to napoprvé prostě nestihnete, nevěšte hlavu, příště (nebo přespříště:/) nejspíš dostanete něco fakt snadného. — Adam
Můžou se Vám hodit tyhle !/Kousky Kódu (když si je vytisknete, na net se totiž mimo dokumentace API nesmí).
Zkouška
Probíhá formou testu (trochu jako C++). Letošní otázky (z 2. 2. 2008) najdete na
fóru. Bodování i obtížnost se od minulého roku trochu změnila, viz otázky z minulého roku (leden 2008), najdete na
velké matfyzácké wiki.
Řešení psacích otázek z 2. 2. 2008:
Pro řešení zaškrtávacích otázek viz vlákno na fóru.
9. Napište metodu, která má dva parametry typu int, hrubou mzdu a daň v procentech, a vrací hodnotu typu double udávající daň k zaplacení. Ověřte, že daň je v rozmezí 0–100 a mzda je nezáporná, pokud parametry nejsou v pořádku vyhoďte výjimku MyException, která je přímým potomkem java.lang.Exception (předpokládá se, že je deklarovaná a importovaná).
Řešení:
public class TaxCalculator{
/* MyException je přímý potomek java.lang.Exception => je třeba deklarovat: */
public double calculateTax(int gross, int taxPercentage) throws MyException{
if( gross < 0 || taxPercentage < 0 || taxPercentage > 100 )
throw new MyException();
/* Pro FP aritmetiku je třeba buď přetypovat nebo použít FP konstantu (tady 100.0): */
return gross * (taxPercentage/100.0);
}
}
10. Napište třídu pro dynamické pole hodnot typu int. Implementujte jen metody pro přidání prvku na konec pole void add(int x) a získání hodnoty prvku int get(int i) (v případě chybného indexu by měla vyvolat výjimku). Pro implementaci použijte skutečné pole hodnot typu int, které se podle potřeby dynamicky realokuje.
Řešení:
public class DynamicArray{
private int[] intArray = new int[0]; // Nejsnažší řešení (netřeba ošetřovat hodnotu null a/nebo vyhazovat výjimky).
public int get(int i){
/*
* Následující přístup může vyhodit ArrayIndexOutOfBoundsException,
* kterou nemá smysl zachytávat jen, abychom ji opět vyhodili.
* Jedná se o runtime exception, takže se ani nedeklaruje v hlavičce.
*/
return intArray[i];
}
public void add(int x){
/*
* Místo ručního kopírování lze použít:
* - java.lang.System.arraycopy(),
* nebo ještě lépe:
* - java.util.Arrays.copyOf(),
* ale to bych asi u zkoušky nedělal, neb si nepamatuju pořadí parametrů.
*/
int[] intArrayX = new int[intArray.length + 1]; // (zvýšení jen o 1 je na implementaci nejjednodušší)
int i = 0;
for(int a : intArray)
intArrayX[i++] = a;
intArrayX[i] = x;
intArray = intArrayX;
}
}
Přehled
Zkouším podle slajdů a dalších zdrojů sepsat přehled Javy (tj. hlavně jazyka), ne vyčerpávající, ale takový, aby stačil někomu, kdo umí C(++), a z velké části pokrýval zkouškové otázky. Můžete taky něco přihodit. — Adam
- Klíčová slova: úplný seznam na sun.com, přehled (v PDF) s info o každém klíčovém slově (s výjimkou později přidaných assert, enum, strictfp, ale pozor: naopak obsahuje true, false, null, což nejsou klíčový slova).
- const a goto jsou (stejně jako v C) klíčová slova, ale (na rozdíl od C) jsou zatím jen vyhrazená, nemají žádný význam.
- Názvy primitivních datových typů jsou klíčová slova, jména hodnot jako false, true a null nejsou.
- Následující klíčová slova C(++) nejsou klíčová slova Javy: asm, auto, bool, delete, explicit, export, extern, false, friend, inline, mutable, namespace, operator, register, signed, sizeof, struct, template, true, typedef, typeid, typename, union, unsigned, using, virtual, wchar_t, cokoli_cast.
- Komentáře jako v C++ (/* ... */, // ...) a javadoc (/** ... */; viz web Sunu).
- Datové typy
- Primitivní datové typy nejsou objekty, ale mají objektové wrappery, od JDK 5.0 se na ně, pokud je to třeba, automaticky konvertují (auto(un)boxing):
Typ | Velikost | Min | Max | Wrapper |
boolean | - | - | - | Boolean |
char | 16 b | Unicode 0 | Unicode 216-1 | Character |
byte | 8 b | -128 | +127 | Byte |
short | 16 b | -215 | +215-1 | Short |
int | 32 b | -231 | +231-1 | Integer |
long | 64 b | -263 | +263-1 | Long |
float | 32 b | IEEE754 | IEEE754 | Float |
double | 64 b | IEEE754 | IEEE754 | DoubleUnicode |
- Od JDK 5.0 výčtový typ enum: enum Matfyzak { INFTIK, MATIK, FYZIK}; /* ... */ public Matfyzak matfizak = Matfyzak.INFTIK;. Na rozdíl od C(++) se jedná o svého druhu třídu, jejíž instance ale nejde dynamicky alokovat. Více viz povídání o enum na sun.com.
- U polí probíhá kontrola mezí, deklarují se typ[] proměnná; nebo typ proměnná[]; nebo kombinací obého (u vícerozměrných polí). Pole se alokují vždy dynamicky (přiřazením new nějakýTyp[délka]), nebo seznamem hodnot v { a }. Délka pole pomocí vlastnosti length (nějakéPole.length). Dá se s tím docela vyblbnout, i když na ukazatele na funkce, které mají jako parametry ukazatele na funkce, které …, v céčku to nemá:
int[]
a = new int[1], // pole typu int[] délky 1, neinicializované
d = {42, 13}, // pole typu int[] délky 2, obsahuje položky 42, 13
b[] = new int[1][1], // pole typu int[][] délky 1, obsahuje pole typu int[] délky 1, neinicializované
c[] = new int[2][], // pole typu int[][] délky 2, neinicializované
d[] = {{4},{2}}, // -//-, obsahuje pole typu int[] {délky 1 obsahující 4, délky 1 obsahující 2}
e[] = { new int[0], {42} }, // -//-, obsahuje { pole typu int[] délky 0, pole typu int[] délky 1 obsahující 42}
f[][][] = {{{{},{}},{},{{}}},{}}; // ...proč ne?
- Řetězce jsou objekty třídy String, od normálních objektů je odlišuje jen možnost zápisu instance literálem a jakoby-přetížený operátor řetězení + (viz Nové objekty a Operátory). Jedná se na rozdíl od C(++) o neměnné řetězce (žádné append() atd., od toho jsou jiné StringBuilder a StringBuffer). Nečekejte také, že byste je mohli porovnávat relačními operátory (porovnáváte můžete pomocí == jen, zda se jedná o týž objekt, viz Operátory).
- Objekty jsou instance objektových tříd (takže mezi ně patří i výčtové konstanty a řetězce).
- Nové objekty se dají dynamicky vytvářet pomocí new: String s = new String(«ahoj»);, dealokují se automaticky garbage collectorem. Jediné objekty, které lze vytvářet zdánlivě staticky, tj. bez new, jsou řetězce a wrappery (pomocí literálů: String iCanHasString = i'm in ur string;, Integer answer = 42;) a instance výčtových typů enum (viz výše).
- Deklarace třídy a metod:
[public] [abstract] [final] class JménoTřídy [extends NějakáNadtřída] [implements NějakýInterface[, ...]] {
/* deklarace atributů: instančních proměnných, nebo třídních ("static") proměnných): */
[public|protected|private] [static] [final] jménoTypu jménoProměnné;
/* např.: */
private boolean b;
String s;
/* konstruktory (jako metody, ale bez návratového typu, jméno shodné se jménem třídy) */
/* metody: obyčejné nebo "static" (třídní, nezávislé na instanci),
* (mohou být přetěžovaná na základě typu parametrů, ne návratové hodnoty): */
[public|protected|private] [static] [final] návratovýTyp|void jméno( seznamParametrů ){
tělo
}
/* abstraktní metody (bez těla, mohou být je v abstraktní třídě): */
abstract [public|protected] návratovýTyp|void jméno( seznamParametrů );
}
- Za povšimnutí stojí:
- Nelze dědit od více tříd. Lze ale implementovat více interfaceů. (Z pohledu C++: jako kdybychom vícenásobnou dědičnost omezili na čistě abstraktní třídy.)
- Implementace metod se hledají dynamicky za běhu. (Z pohledu C++: jako kdyby všechny metody byly deklarované virtual.)
- Abstraktní metody nesmí být static ani private a mohou být jen v abstraktní třídě (abstract). Bez ohledu na to, zda abstraktní třída má abstraktní metody, nelze vytvářet její instance. Abstraktní třídy se ale nepoužívají tak často jako v C++. (Jsou k dispozici interfacey.)
- Jediný modifikátor přístupu, který třída na nejvyšší úrovni souboru může mít, je public. Nemá-li ho, je implicitně tzv. package-private, viz dále.
- Deklarace tříd, kontrola přístupu, inicializace, vnitřní třídy apod. podrobněji viz Classes and Objects (sun.com), nebo zkuste vnitřní třídy trochu odlehčeně.
- Viditelnost tříd: Balíky, packages, sestávají z více tříd a tvoří prostory viditelnosti (nadřazené souborům, které jsou zase nadřazené třídám).
- Příslušnost souboru zdrojového kódu k balíku: package jménoBalíku;, kde jménoBalíku bývá obrácená doména (info.lokiware.mff.balik).
- Třídy v rámci balíku se označují krátkým jménem. Třídy z jiných balíků plným jménem: jméno.balíku.JménoTřídy nebo se dají importovat: import jméno.balíku.JménoJednéTřídy; import jméno.balíku.*; import static jméno.balíku.JménoTřídy.JMÉNO_STATIC_PROMĚNNÉ. Balík java.lang je importován vždy.
- Každá public třída musí být ve svém souboru JménoTřídy.java a je vidět i mimo balík (balík viz dále). Třídy s implicitním přístupem (tj. ne-public) jsou vidět jen v rámci balíku, říká se jim pak package-private. (S přístupem private lze deklarovat jen vnitřní třídu.) Hierarchii jmen balíků odpovídá hierarchie adresářů se se zdrojovými soubory.
Inicializace proměnných: Pokud explicitně není v deklaraci, používají se pro instanční proměnné implicitní hodnoty (false pro booleany, 0 pro číselé typy, null pro reference na objekty), lokální proměnné jsou neinicializované (pokus o přečtení hodnoty vede k chybě překladu). Pokud nevíte, k čemu všemu může být final (zvlášť u lokálních proměnných), doporučuju tohle povídání o final.
Viditelnost lokálních proměnných: v rámci bloku ({ a }), pokus o zastínění novou deklarací ve vnořeném bloku ({X x; { X x; }}) vyvolá chybu při překladu, na rozdíl od C(++).
Metody se volají pomocí tečky (.) (foo.doSomething(42)). Parametry se předávají hodnotou stejně jako se přiřazují hodnoty proměnným. Více viz třeba povídání na JavaRanch.com. [U primitivních typů se předá jejich skutečná hodnota, u objektů je hodnotou odkaz (tzn. odpovídá to & v C+) a toto chování nelze (narozdíl od C++) změnit. Takže pokud nechceme, aby metoda měnila objekt, je nutné explicitně vytvořit kopii, pro použití primitivních typů jako výstupních parametrů je nutné je zakrabicovat.]
Dobrá poznámka k těm referencím, ale to «zakrabicování» dává smysl pouze, pokud tím myslíš zakrabicování do vlastní třídy obalující nějaký primitivní typ. Obsah objektů tříd Boolean, Integer apod. totiž stejně nelze měnit (jednak na to nejsou metody, jednak odpovídající proměnné jsou deklarované private final —
Adam
Ha, to jsem netušil. Takže děkuji za osvětlení (nebo snad osvícení?). — Vojta
Operátory a jejich priorita je shodná s aritmetickými, relačními, logickými, bitovými a přiřazovacími operátory v C s následujícími rozdíly:
- Bitový posun vpravo (>> a >>=) doplňuje zprava opakováním «znaménkového» bitu (v C není pro znaménkové typy definováno), pro doplňování nulami slouží zvláštní operátory >>> a >>>=
- Výraz objekt instanceof TřídaNeboInterface se vyhodnotí jako true, pokud je objekt instancí dané třídy (resp. její podtřídy) nebo třídy implementující daný interface, jinak vrací false.
- Operátory == a = zacházejí s referencemi na objekty (tj. všemi «objektovými» hodnotami v Javě!) jako by to byly ukazatele. (Ukazatelovou aritmetiku, porovnávání ani jiné podobné triky ovšem dělat nejde, v tom jsou reference zase jako reference v C++).
- Logické operátory lze aplikovat jen na logické hodnoty, nedochází na rozdíl od C++ k automatické konverzi mezi číselnými typy (případně ukazateli) a boolean. (Není možné ani přetypovat: (int) true, (boolean) 1.) Bitové operátory, s výjimkou doplňku (~) naopak lze bez problémů použít na logické (boolean) hodnoty, které se pak chovají jako by měly jeden bit (1 pro true, 0 pro false, na rozdíl od C++), vede to k nezkrácenému vyhodnocení.
- V cyklech for lze použít čárku (,) v první a třetí části (ne v podmínce), jako kdyby to byl C-čkový operátor ,, jinde ale ne, nejedná se o operátor.
- Přetypování (typ), indexování pole pole[index], volání funkce f(args), přístup přes . a vytvoření instance pomocí new se provádí stejně jako v C (jen se formálně nejedná o operátory). Jiné operátory C(++) v Javě nejsou (::, ->, sizeof, delete, typeid, něco_cast ani už jmenovaná ,).
- Operátory se nedají přetěžovat. Jediný, v jistém slova smyslu, přetížený operátor je operátor +, kterým lze spojovat řetězce.
Konstrukce pro řízení toku se liší od C++ v následujících bodech:
- continue a break (ale ne goto!) je možné použít spolu s návěstím pro přerušení/pokračování některého s nadřazených cyklů:
návěstíVenku: for(...){ // vnější cyklus
while(...){ // vnitřní cyklus
break;
continue;
continue návěstíVenku; // pokračuje novou iterací vnějšího cyklu
}
}
- U polí a tříd s iterátorem lze od JDK 5.0 použít foreach pomocí dvojtečky (není na to ale zvláštní klíčové slovo), např.: X[] poleX = new X[42]; for(X x : PoleX) /*...*/;
- Rozskok switchem funguje pro primitivní typy byte, short, char a int (a odpovídající wrapper třídy a pro výčtové typy (enum).
Ke zkoušce se určitě ještě podívejte na výjimky a věci související s vlákny.