Java – wstęp do języka

advertisement
Java – wstęp do języka
Andrzej Chybicki
[email protected]
Pok. 738
To co już powinniśmy wiedzieć…
• Składnia, gramatyka i doświadczenie w
programowaniu w C/C++
• Idea i zasada programowania obiektowego
(C++) (klasy, zmienne)
• Podstawowe mechanizmy dziedziczenia
• Zakładam, że student nie napisał żadnej
aplikacji w języku Java – java od podstaw
Historia Javy
• Początki języka Java sięgają roku 1990, gdy Bill Joy
napisał dokument pod tytułem “Further”, w którym
sugerował inżynierom Sun Microsystems stworzenie
obiektowego środowiska w oparciu o C++. Dokument ten
miał pewien wpływ na twórców projektu Green (James
Gosling, Patrick Naughton i Mike Sheridan). W roku 1991
w ramach projektu Green opracowano w języku C
kompilator oraz interpretator wynalezionego przez
Goslinga języka OAK (Object Application Kernel), który
miał być narzędziem do oprogramowania “inteligentnych”
konsumenckich urządzeń elektronicznych. Ponieważ
nazwa “OAK” okazała się zastrzeżona, zmieniono ją na
“Java”.
Java – język czy coś więcej?
• Java oczywiście jest językiem
programowania, lecz w praktyce na Javę
składają się inne elementy funkcjonalne:
1.
2.
3.
4.
Kompilator
Maszyna wirtualna „Java” (JVM).
Biblioteki języka Java
Obiektowy język programowania bazujący na
składni języka C++
1.Kompilator
• Kompilator który przetwarza program
“nazwa.java” na tak zwany B-kod (bytecode, Jcode), zapisywany automatycznie w plikach z
rozszerzeniem nazwy “.class”. B-kod jest
przenośną postacią programu, która może być
zinterpretowana przez odpowiednią maszynę
wirtualną, to jest “urządzenie logiczne”, na którym
będzie wykonywany program binarny
2. Maszyna wirtualna Javy (ang.
JVM – Java Virtual Machine)
•
JVM można uważać za abstrakcyjny komputer, który wykonuje
programy, zapisane w plikach z rozszerzeniem nazwy “.class”.
Maszyna wirtualna może być implementowana na rzeczywistych
komputerach na wiele sposobów, na przykład jako interpretator
wbudowany w przeglądarkę WWW (np. Netscape), lub jako
oddzielny program, który interpretuje pliki “nazwa.class”. Może to
być także implementacja polegająca na przekształceniu tuż przed
rozpoczęciem fazy wykonania pliku z B-kodem na program
wykonalny, specyficzny dla danej maszyny. Mechanizm ten można
określić jako tworzenie kodu wykonalnego w locie (ang. Just-InTime, np. kompilator JIT firmy Symantec). Interpretatory B-kodu,
tj. różne maszyny wirtualne, są także często napisane w języku Java.
JVM
• JVM nie jest osobną maszyną w sensie
sprzętowym, lecz w sensie programowym
• Można powiedzieć, że JVM jest nakładką
na system umożliwiającą kompilowanie i
wykonywanie aplikacji Java.
• JVM a szybkość… inicjalizacja i
kompilowanie JIST
JVM a OS
Java a niezależność sprzętowoprogramowa
MyProgram.java
Kompilator
Edytor
MyProgram.class
Interpretator
Interpretator
PC - Windows XP/NT
SUN - Solaris
Język Java
•
•
•
•
Oparty o składnię C++
Prawdziwie obiektowy (tylko obiektowy!!)
Podział na klasy
Jedna klasa główna – metoda „main”, która
stanowi „wejście” do programu
• Program w języku „Java” nazywa się
aplikacją (samodzielna) lub applet
(aplikacja sieciowa)
Aplikacje Java
• Warunek uruchomienia: zainstalowanie na
komputerze JDK (Java Developement Kit) lub
JRE (ang. Java Runtime Enviroment).
• Każda aplikacja musi zawierać dokładnie jeden
moduł źródłowy nazywany modułem głównym
aplikacji zawierającym definicje klas, w którym
jedna z klas zawiera publiczną funkcję klasy
(funkcje takie są poprzedzane słowem kluczowym
static) main.
• Funkcje w języku Java są zawsze własnością klas
lub obiektów i nazywa się je metodami (ang.
methods)
• Plik który zawiera metodą main jest
„wejściem” do aplikacji Java. Musi on
posiadać tę samą nazwę co nazwa klasy, która
posiada metodę main.
• Aplikacja składająca się z wielu pakietów
(modułów) i klas może posiadać kilka klas z
metodą „main”.
• Metoda main musi być publiczna i statyczna
– public static void main(String args[])
• Przykładowy program wygląda następująco:
Kompilacja i uruchomienie aplikacji
Java
• Kompilacja programu: javac Hello.java , o ile
mamy prawidłowo ścieżkę ustaloną do javac
• Udana kompilacja wygeneruje plik z B-kodem o
nazwie Hello.class, zawierający sekwencję
instrukcji dla interpretatora JVM. Kod ten
wykonujemy przez wywołanie interpretatora o
nazwie java poleceniem:
java Hello
Omówienie przykladu
• Interpretator wyszuka plik o nazwie Hello.class,
ustali, czy klasa Hello zawiera publiczną metodę
statyczną main i wykona instrukcje zawarte w
bloku main.
• Nie ma wielkości globalnych: wszystkie zmienne i
stale są własnością klas lub obiektów.
• Wartością parametru arg[0] jest pierwszy po
nazwie programu spójny ciąg znaków
Konsola w Javie – klasa system
• Klasa System zawiera statyczny obiekt składowy typu
PrintStream o nazwie out; wywołanie System.out
oznacza pisanie do standardowego strumienia
wyjściowego. Klasa PrintStream zawiera szereg
przeciążeń metody o nazwie print; jedno z nich przyjmuje
parametr typu String. Kompilator automatycznie tłumaczy
literał stały Hello, World\n na odpowiedni obiekt
klasy String; odnośnik (referencja) do tego obiektu jest
przekazywana do metody System.out.print(). Metoda
print() generuje jeden wiersz wyjściowy i powraca do
metody main, która kończy wykonanie programu.
• Standardowe wyjście z poziomu programu wygląda tak
samo : System.out.* niezależnie od systemu i sposobu
uruchomienia programu .
Identyfikatory i znaki specjalne
• Słowa zarezerwowane (true, false, if ,
switch, case…) – bazujące zasadniczo na
składni C/C++
• Słowa kluczowe (abstract, final) – można
używać tylko w ściśle określonym
kontekście
Zastrzeżone słowa w Java
abstract
const*
float
long
static
try
assert
Continue
for
native
strictfp**
void
boolean
Default
goto*
new
super
volatile
break
Do
if
package
switch
while
Byte
Double
implements
private
synchronized
Case
Else
import
protected
this
catch
Extends
instanceof
public
throw
Char
Final
int
return
throws
class
Finally
interface
short
transient
Klasy w Javie
Definicja klasy ma postać:
Deklaracja klasy
{
Ciało klasy
}
Klasy
• Słowo kluczowe class można poprzedzić slowami:
abstract, public, final
• Po słowie class można umieścić wyrażenia:
• ‘extends nazwa_superklasy’ oraz ‘implements
nazwy_interfejsów’
• Mechanizm dziedziczenia już omówiliśmy
• Mechanizm interfejsów w Javie dopiero
omówimy.
• Java nie umożliwia wielodziedziczenia ale można
to zrobić używając mechanizmów interfejsów
Klasa Object
• Uwaga. W języku Java każda klasa
dziedziczy od predefiniowanej klasy Object.
Zatem, jeżeli w definicji klasy nie występuje
fraza extends, to jest to równoważne
niejawnemu wystąpieniu w tej definicji frazy
‘extends Object’
•
•
•
•
•
•
•
•
•
•
protected Object clone()
boolean equals (Object obj)
void finalize()
Class getClass()
Int hashCode()
void notify()
void notifyAll()
String toString()
void wait()
void wait (long timeout)
Zmienne klasy
• Zmienne klasy (statyczne, tj. poprzedzone słowem
kluczowym static), konstruktor i metody oraz
funkcje klasy (statyczne) także są poprzedzanie
słowem static.
• Nazwa każdej zmiennej składowej, zmiennej
klasy, metody lub funkcji klasy musi być
poprzedzona nazwą typu (np. boolean, double,
char, float, int, long, void).
• Zmienne i metody statyczne są traktowane
analogicznie jak w C++
Dostęp do zmiennych i metod klasy
•
•
•
•
private
protected
public
niezdefiniowany
Zmienne i metody
• Zmienne i metody typu static
– przypomnienie
• Zmienne i metody final
– Zmienne ,która nie może być modyfikowana
– Metoda, która nie może redefiniowana w klasach
dziedziczących
– Klasa po której nie można dziedziczyć
– Lokalne zmienne finalne, które zostały zadeklarowane,
ale jeszcze nie zainicjowane, nazywa się blank final.
Słowa kluczowe this i super
• Dostęp do elementów klasy uzyskuje się za
pomocą operatora kropkowego. Jeżeli
element danej klasy (zmienna lub metoda)
przesłania (overrides) jakiś element swojej
superklasy, to można się do niego odwołać
za pomocą słowa kluczowego super, jak w
poniższym przykładzie
• Spójrzmy na przykład:
Omówienie przykładu
• ASillyClass oob;
• oob = new ASillyClass();
• Większość klas (wbudowanych lub
definiowanych przez użytkownika)
inicjalizujemy za pomocą operatora new
• Do kontrolowania ilości obiektów danej
klasy czasami są używane tzw. Fabryki
obiektów.
Konstruktory w Javie
• Konstruktory w Javie definiujemy analogicznie
jak w C++. Konstruktor musi być zawsze jawny i
zwracać obiekt klasy w jakiej jest inicjowany.
• W javie nie ma destruktorów obiektów
– Wady
– Zalety
• „Utylizacją” nieużywanych obiektów w Java
zajmuje się mechanizm „Garbage collector” –
omówimy go później
Konstruktory w Javie
• Jeżeli dana klasa nie zawiera deklaracji konstruktorów, to
kompilator dostarcza konstruktor domyślny z pustym
wykazem argumentów, który w swoim bloku wywołuje
konstruktor super() jej bezpośredniej nadklasy. Weźmy dla
ilustracji definicję klasy Point:
public class Point { int x, ,y; }
• Jest ona równoważna definicji
public class Point { int x, ,y; public Point() { super(); } }
z niejawnym wywołaniem dostarczanego przez kompilator
konstruktora superklasy, od której bezpośrednio dziedziczy
klasa Point.
Dostęp do metod i pól superklasy
(klasy bazowej)
• Odwołanie się do pól i metod superklasy odbywa
się poprzez słowo kluczowe super.
• Czasami ważne jest mieć pewność ze odwołujemy
się do pól i metod obiektu w którym aktualnie
jesteśmy – w takim przypadku używamy słowa
kluczowego this
• Operator this zawsze zwraca obiekt w którym
znajduje się aktualnie wykonana instrukcja.
Niejawne wywoływania
konstruktorów superklasy
• Spójrzmy na przykład:
Konstruktor super() może być
również wywoływany jawnie
• Spójrzmy na przykład
Konstruktory – this(…) i super(…)
• Uwaga. instrukcja this(argumenty); musi
być pierwszą instrukcją w ciele
konstruktora lub metody; to samo dotyczy
instrukcji super(argumenty);.
Sprzątanie obiektów w Java
• Inicjalizacja obiektów występuje zawsze
kiedy chcemy ich użyć – co do tego każdy
programista jest świadomy
• Garbage Collector - gc (odśmiecacz
pamięci) automatycznie pozbywa się
obiektów nieużywanych – ale nie zawsze
powoduje zwolnienie całej pamięci
wykorzystywanej przez te obiekty
Szczególny przypadek
• Odśmiecacz zwalnia tylko pamięć
zalokowaną przez obiekt z wykorzystaniem
operatora new
• Jeśli obiekt w inny sposób alokuje pamięć
odśmiecacz może sobie nie dać rady
• Rozwiązanie – metoda finalize()
Używamy metody finalize()
• Jeżeli obiekt posiada metoda finalize() to
odśmiecacz będąc gotowy do zwolnienia
obiektu najpierw wywoła metodę finalize()
• Dopiero przy kolejnym odśmiecaniu zażąda
zwrotu pamięci samego obiektu
• Zdefiniowanie metody finalize() leży w
gestii użytkownika!!
Finalize() a destruktory C++
• Finalize() i destruktory C++ to nie to samo!!
• C++ - destruktor zawsze niszczy obiekt
• Finalize powoduje jedynie zwolnienie ewentualnej
pamięci lub uchwyty do pliku, zasobu sieciowego
– ale nie niszczy obiektu
• Kiedy i jak używamy finalize()?
• Używamy przed zniszczeniem obiektu, aby
ewentualne zasoby związane z obiektem których
gc nie wyczyści samemu zwolnić
• Spójrzmy na przykład:
Wywolanie finalize()
Wynik
Tytul ksiazki
Finalizujemy obiekt przy index = 3
W powyższym przykładzie ręcznie
uruchomiliśmy gc , ale JVM automatycznie
uruchamia gc w przypadku alokacji lub
zwalniania dużej ilości zasobów
Zasada działania gc
• Zasada działania sterty w C++ - plac w
którym poszczególne obiekty zajmują
pamięć (powierzchnię) potrzebną im do
życia
• W Java stertę można porównać do
przesuwającej się taśmy, w której każdy
nowy obiekt alokuje nową część taśmy – co
powoduje szybkie wyczerpanie pamięci
Sztuczki w gc – mechanizmy
oszczędzania pamięci
• Gc podczas „zbierania śmieci” porządkuje
obiekty w obszar ciągły przesuwając
wskaźnik taśmy.
• Są różne techniki odśmiecania , które
obiekty wyrzucać, które zostawiać, jak to
sprawdzać?
• Liczenie referencji
• Zliczanie odwołań
Tablice w Java
• Każda tablica w Java jest obiektem
• Dynamicznie tworzona – nie ma problemu
zarządzania pamięcią (wyręcza nas w tym
kompilator i JVM)
• Zmienna prezentująca tablicę jest tak
naprawdę referencją do pamięci pierwszego
z elementów tablicy (jak w C/C++)
Tablice – ciąg dalszy
• Obiekty lub składowe tablicy nazywa się elementami
• Każdy obiekt tablicowy zawiera pole length zawierający
ilość elementów w tablicy
• Elementy są numerowane od 0
• Niezainicjowane (ale zadeklarowane!!) elementy tablicy
mają inicjalną wartość = 0
• Elementami tablic mogą być typy proste (int, float ,
double) oraz typy złożone (obiekty – nie klasy), null’e,
• Tablice mogą być inicjowane jako tablice elementów klasy
abstrakcyjnej pod warunkiem… no właśnie - jakim
warunkiem??
I jeszcze parę istotnych uwag o
tablicach
• Semantyka :
• Deklaracja nośnika tablicy:
int[] tab;
• Zainicjowane
tab = new int[3];
• Albo od razu:
int[] tab = new int[3];
• Inny sposób:
int[] tab = {0, 0, 0 };
• Spójrzmy na przykład:
Wynik i dyskusja
ia[0] = 1
ia[1] = 2
ia[2] = 3
da[0] = 1.3
da[1] = 2.5
da[2] = 3.4
Klasa obiektu ia: class [I
Klasa obiektu da: class [D
class java.lang.Object
Drobna uwaga
• Dla tablicy wielowymiarowej sygnatura
byłaby poprzedzona odpowiednią liczbą
nawiasów prostokątnych. Np. instrukcja
• System.out.println("Klasa obiektu: "+(new
float [3][4][5]).getClass());
da wydruk:
• Klasa obiektu: class [[[F
Tablica typów zdefiniowanych przez
użytkownika
• W Javie elementami tablic mogą być
zarówno typy proste jak i obiekty klas
wbudowanych i zdefiniowanych przez
użytkownika.
• Spójrzmy na przykład:
W wyniku działania programu
otrzymujemy
mytab[0].x = 20
mytab[1].x = 21
mytab[2].x = 22
mytab jest obiektem typu MyClass[].
class [LMyClass
class java.lang.Object
Tablice wielowymiarowe
• Tablice mogą zawierać elementy dowolnego typu
a więc i także tablice, które składają się tez z tablic
tablic obiektów etc, etc, - wniosek:
• W Java też można tworzyć tablice
wielowymiarowe
• Odwoływanie się do elementów tablic
analogicznie jak w C++
• Przykład tablicy wielowymiarowej
Tablice wielowymiarowe w Java cd
int[][] tab = { {1,2,3}, {4,5,6} };
for(int row=0; row < 2; row++) {
for(int col=0; col < 3; col++) {
System.out.println(tab[row][col]);
}
}
Tablice a kolekcje obiektów
• Każdy program musi przechowywać określoną
ilość obiektów.
• Tablice są jednym ze sposobów przetrzymywania
dużej ilości obiektów
• Tablica jest w Java najbardziej efektywnym
sposobem przechowywania REFERENCJI do
dużej ilości obiektów
• No ale co z wygodą? (Przeszukiwanie,
wydobywanie, zwiększanie, kopiowanie itp. , itp.)
• Rozwiązanie – klasy kontenerowe
Klasy kontenerowe
• Klasy zapewniające mechanizmy łatwego
przechowywania, udostępniania,
zapisywania dużej ilości obiektów (często
różnych klas).
AC1
Przykłady klas kontenerowych
• Collection – służy do przechowywania
pojedynczych elementów
• List – lista obiektów w kolejności
• Set – zbiór obiektów ( bez kolejności)
• ArrayList – bardziej zaawansowana lista
• HashMap – lista asocjacyjna
• Inne…
Slajd 59
AC1
Dodać częśc o Collection i Arraylistach
Andrzej Chybicki; 2006-11-07
Uwagi odnośnie kontenerów (klas
kontenerowych)
• Ponieważ do obiektów możemy wkładać obiekty
dowolnej klasy w praktyce tracimy informacje o
typie obiektów wydobywanych z kontenerów
• Po wydobyciu obiektu trzeba wykonać rzutowanie
do odpowiedniego typu
• Programista sam musi pamiętać jakiego typu
obiekty są zapisane w środku (chociaż są wyjątki)
Operowanie na kontenerach iteratory
• Uniknięcie korzystania z pętli for, while
podczas przeszukiwania kontenerów
• Wykorzystanie wbudowanych
mechanizmów przeglądania kontenerów –
szybsze, wygodniejsze i łatwiejsze
(przeważnie).
• Spójrzmy na przykład:
Interfejsy (ang. Interfaces)
• Co to jest interfejs i przede wszystkim do
czego służy?
• Semantyka i technika używania interfejsów
Co to jest interfejs?
Konstrukcja o postaci
interface nazwa {
/* Deklaracje metod i definicje stałych */
}
jest w języku Java typem definiowanym
przez użytkownika.
• Interfejs nie jest klasą, jest to osobny typ, który definiuje
użytkownik (programista)
• Można traktować interfejs jako klasę abstrakcyjną, z
sygnaturami metod i ewentualnie pól
• Interfejs tworzy klasę
• Interfejs stanowi szablon wszystkich klas, które go
implementują
• W interfejsie nie definiujemy metod – jedynie je
deklarujemy.
• Definicja (ciało) metody znajduje się w klasie która
implementuje interfejs
• Spójrzmy na przykład:
Omówienie
• W ogólności w definicji interfejsu wszystkie
metody są domyślnie public i abstract,
• Stale są domyślnie public, static i final.
• zabrania się używania specyfikatorów private i
protected
• Dostęp do zadeklarowanej w interfejsie metody
lub stałej uzyskuje się za pomocą operatora
kropkowego, Np. Sleeper.ONE_SECOND.
Przykład interfejsu
Definicja w klasie implementującej
I teraz co z tym możemy zrobić…
• Poprawne będą wówczas deklaracje:
Plane biplane = new Plane();
biplane.takeOff();
Boat vessel = new Boat();
vessel.swim();
• a także deklaracje:
PlaneLike aircraft = new Plane();
aircraft.takeOff();
BoatLike motorboat = new Boat();
motorboat.swim();
Do czego w praktyce używa się
interfejsów
• Najczęściej jako listenery (myszki, klawiatury) ,
używające wbudowanych interfejsów języka Java
• MouseListener, MouseMotionListener,
KeyboardListener
• Zdarzenie (np. kliknięcie lub ruch myszki,
naciśnięcie przycisku klawiatury) są
przechwytywane przez klasę implementującą
wybrany interfejs i obsługiwane przez nią.
Spójrzmy na przykład:
Inne praktyczne zastosowania
• Komponenty, które wykorzystują listenery
Button, JButton, Label, Panel, TextField i
inne
• Przykladowe listenery:
KeyListener, MouseListener,
MouseMotionListener, MouseMoveListener i
inne…
Inne sposoby użycia interfejsów wielodziedzicznie
• Załóżmy, iż chcemy mieć obiekt (klasa
SeaPlane), który czasami zachowuje się jak
samolot a czasami jak łódź.
• Java nie posiada mechanizmu
wielodziedziczenia.
• Jak osiągnąć to poprzez mechanizmy Java?
Spójrzmy na przykład:
• Chcemy osiągnąć efekt taki jak pokazano
na rysunku w części a)
• Uzyskujemy to w sposób jaki pokazano na
rys b)
a)
b)
Vehicle
Plane
Vehicle
Boat
Plane
Boat
interface
BoatLike
SeaPlane
interface
PlaneLike
SeaPlane
Dziedziczenie interfejsów
Interfejs nie może dziedziczyć klas, ale może dziedziczyć dowolnie wiele
interfejsów. Np. korzystając z podanych wyżej definicji moglibyśmy
utworzyć interfejs
interface SeaPlaneLike extends PlaneLike, BoatLike{
public long SPEED_LIMIT = 1000;
}
i wykorzystać go w klasie SeaPlane, implementując metody
zadeklarowane w interfejsach PlaneLike i BoatLike. Zauważmy przy
okazji, że klasy implementujące dany interfejs mogą traktować
zadeklarowane w nim stałe tak, jak gdyby były one odziedziczone.
A jak to wygląda w praktyce?
• Używanie interfejsów głównie wbudowanych (listenery,
EJB i inne komponenty)
• Wielodziedziczenie – w praktyce zmienia się hierarchię
dziedziczenia, raczej nie implementuje się mechanizmu
wielodziedziczniea
• Klasy abstrakcyjne – używa się głównie aby program był
przejrzysty
• Mechnizmy (interfejsy, static, private, protected) – można
nie używać – ale dobre wykorzystanie ułatwia czasami
programowanie i zmniejsza ilość kodu
Organizacja kodu w Java
Pliki źródłowe i pakiety czyli
organizacja kodu w Java
• Większe programy składają się ze zdecydowanie więcej
niż jednej klasy (kilkaset, kilka tysięcy klas)
• Jak już powiedzieliśmy każda aplikacja musi mieć
przynajmniej jedną klasę z metodą public static void main,
musi mieć on tę samą co nazwa pliku (inaczej jest w
przypadku appletów i midletów)
• Nie jest to wymagane ale dla przejrzystości zaleca się aby
w jednym pliku źródłowym była implementacja jednej
klasy.
• Jeśli w pliku znajduje się więcej niż definicją jednej klasy
w wyniku kompilacji otrzymamy więcej plików z Bkodem *.class. To samo tyczy się klas zagnieżdżonych.
Pakiety (ang. Packages)
• Wszystkie pliki (moduły źródłowe) są niezależnie
kompilowalne. (uruchamianie z błędami w
kompilacji).
• Moduł źródłowy, w którym definicje klas oraz
interfejsów poprzedzono deklaracją pakietu o
postaci package nazwa_pakietu; staje się
pakietem.
• Deklaracja pakietu rozszerza przestrzeń nazw
programu i pozwala na lepsze zarządzanie
programem wielomodułowym
Organizacja pakietów
•
•
•
•
•
Pakiety a system plików projektu
Jak dzielić na pakiety?
Jakie przydzielać nazwy pakietom?
Pakiety a klasy i dziedziczenie?
Pakiety a moduły?
Pakiety i pliki w praktyce
• Spójrzmy na przykład:
Pakiety są ściśle powiązane z katalogami, w których
umieszcza się moduły źródłowe i pliki wynikowe.
Załóżmy np., że w katalogu C:\javaprog (Win’98 DOS)
umieszczono główny plik źródłowy aplikacji Student.java,
który importuje z katalogu C:\javaprog\myprog\pakiet1
klasy HiGrade oraz LoGrade, umieszczone w plikach
HiGrade.java i LoGrade.java. Deklaracje importu mogą
mieć postać:
import myprog.pakiet1.HiGrade;
import myprog.pakiet1.LoGrade;
Omówienie przykładu – istotna
uwaga
•
Uwaga. Deklaracja importu nie oznacza włączania do
pliku Student.java tekstu zawartego w plikach
HiGrade.java i LoGrade.java. Natomiast pozwala ona
użytkownikowi klasy Student używać skrótowych nazw:
np. zamiast pisać myprog\pakiet1\HiGrade highgrade
= new javaprog\myprog\pakiet1\HiGrade(); mogliśmy
napisać krótko: HiGrade highgrade = new HiGrade();.
Gdybyśmy chcieli używać również klasy Empty (lub
innych klas pakietu pakiet1), to deklaracja importu
miałaby postać: import mike.myprog.pakiet1.*. W
języku Java ważna jest także kolejność deklaracji:
najpierw deklaracja pakietu, po niej deklaracje
importu, po czym definicje klas.
Pakiety wbudowane (biblioteki)
• Podstawowe pakiety J2SE
java.applet, java.awt, java.beans, java.io,
java.lang, java.math, java.net, java.rmi,
java.security, java.sql, java.text, java.util,
javax.accessibility, javax.swing, org.omg
Klasy zagnieżdżone
• Począwszy od wersji 1.1 języka Java
wprowadzono możliwość definiowania klas
wewnętrznych (inner classes) jako
elementów składowych innych klas;
• Można je definiować albo lokalnie
wewnątrz ciała klasy zewnętrznej, albo
(anonimowo) jako wyrażenie w bloku
Cechy klas zagnieżdżonych
•
•
Nazwy klasy wewnętrznej nie można użyć na
zewnątrz jej zasięgu. Pomaga to w
strukturalizacji klas w obrębie pakietu.
Kod klasy wewnętrznej może wykorzystywać
proste nazwy z zasięgów otaczających, w tym
zarówno nazwy zmiennych składowych klasy,
jak i nazwy zmiennych wystąpienia klas
otaczających oraz zmienne lokalne otaczających
bloków.
Klasy wewnętrzne a pola i metody
static
• Czy można definiować pola static w klasie wewnętrznej?
• Zwykle klasa wewnętrzna nie może deklarować żadnej ze
swoich składowych ze słowem kluczowym static,
ponieważ ciało klasy wewnętrznej jest w zasięgu klasy
otaczającej (patrz pierwszy slajd)
• W rezultacie zmienne statyczne dla klasy wewnętrznej
muszą być umieszczane w klasie otaczającej
• To samo tyczy się metod
• Klasa wewnętrzna ( przeciwieństwie do normalnej) może
być statyczna!! (patrz drugi slajd)
Jeszcze parę informacji…
• Jeżeli klasa wewnętrzna posiada nazwę, może być
deklarowana ze słowami kluczowymi private, protected,
final, lub abstract. Natomiast klasy anonimowe są
prywatne w bloku, w którym są zadeklarowane, ponieważ
nie mogą być wykorzystane na zewnątrz tego bloku.
• Zagnieżdżanie klas w ten właśnie sposób pozwala
dowolnej klasie wysokiego poziomu dla logicznie
powiązanych klas poziomu niższego uzyskać podobną do
pakietu organizację, w której wszystkie klasy mają pełny
dostęp do pól prywatnych.
Kompilacja klas zagnieżdżonych
• Jak nie mylić klas zewnętrznych o tej samej nazwie z
klasami wewnętrznymi innych klas?
• Nazwy klas wewnętrznych są przekształcane aby uniknąć
dwuznaczności
• Nazwy są kodowane przez kompilator, który bierze ich
postać źródłową, kwalifikuje je kropkami, po czym
zamienia każdą kropkę po nazwie klasy na znak dolara
(‘$’).
• Tak więc po kompilacji pliku źródłowego “Outer1.java”
otrzymamy dwa pliki z B-kodem: “Outer1.class” oraz
“Outer1$Inner.class”. Oczywiście interpretacji poddamy
plik “Outer1.class”.
• Istnienie klas zagnieżdżonych wymaga pewnej
zmiany w nazwach kwalifikowanych przy
definiowaniu zmiennych typu klasy
wewnętrznej oraz hierarchii dziedziczenia.
Istotne zmiany to:
– Inicjowanie zmiennych odnośnikowych typu klasy
wewnętrznej słowem kluczowym this,
odpowiadającym bieżącemu obiektowi klasy
zewnętrznej.
– Kwalifikowanie dziedziczenia od klasy wewnętrznej
jej nazwą, umieszczaną po nazwie klasy zewnętrznej
i kropce.
• Ilustracją tego sposobu kwalifikacji nazw jest
podany niżej przykład.
Omówienie przykladu
• W wyniku kompilacji zostaną utworzone
cztery pliki z B-kodem: “Vehicle.class”,
“Vehicle$Wheel.class”,
“WireRimWheel.class” oraz “Auto.class”.
Mechanizm wyjątków w Javie (ang.
Exceptions)
• Programista tworząc aplikację nie zawsze jest w stanie w
pełni przewidzieć jej zachowanie, w szczególności gdy ma
do czynienia ze zdarzeniami od aplikacji niezależnymi np.
użytkownik, sieć, obsługą urządzenia zewnętrznego
• Język Java umożliwia zabezpieczenie się przez
nieprzewidzianymi błędami jakie mogą wystąpić podczas
działania programu.
• W przypadku wystąpienia błędu sterowanie programu
wychodzi z aktualnie wykonywanej metody i przechodzi
do specjalnego bloku obsługi wyjątku (try i catch).
• Mechanizm obsługi wyjątków zwiększa niezawodność
programów Java.
Wykrywanie błędów w programie
• Oczywiście najlepiej jest wykrywać błędy już w
fazie kompilacji, ale nie zawsze programy
kompilowalne są poprawne.
• Fajnie, jest gdy możemy obsłużyć
nieprzewidziany przez nas błąd podczas działania
programu a nie podczas kompilacji
• Filozofia Java obsługi błędów polega na
wymuszeni na programiście pewnych zachowań
zwiększających jakość aplikacji (czyt. programista
często jest zmuszany do obsługi błędów przy
wywoływaniu niektórych metod).
Filozofia wyjątku
• Wiemy, że w danym momencie może
wystąpić wyjątek – i wiemy, że nie wiemy
co z nim zrobić …
• Przekażmy więc ten problem do innego
bloku programu ( argumentem przekazania
jest wyjątek zawierający opis błędu) , który
wie co z tym błędem zrobić…
Kiedy obsłużyć wyjątek
• Programista sam może (zaraz o tym powiemy)
decydować czy w danym momencie ma być
„wyrzucany” wyjątek czy też nie.
• ZASADA:
Wyjątek jest wtedy gdy w danym momencie
przetwarzania nie mamy wystarczająco informacji
aby poradzić sobie z danym problemem.
Wyjątki
• W języku Java istnieje bardzo rozbudowana
hierarchia (drzewo) predefiniowanych klas
wyjątków, których superklasą jest klasa
Throwable
• Można definiować sobie własne wyjątki
dziedziczące po klasie Throwable
No a jak to wygląda w praktyce
• Dla obsługi wyjątków wprowadzono pięć słów kluczowych: throw,
throws, try, catch i finally. Słowo kluczowe throw służy do jawnego
zgłaszania wyjątków nieweryfikowalnych i występuje w instrukcji
throw o składni throw wyrażenie; gdzie wyrażenie musi oznaczać
zmienną lub wartość typu referencyjnego do klasy Throwable lub jej
podklas.
• Zgłoszenie wyjątku w instrukcji throw spowoduje natychmiastowe
opuszczenie bloku lub funkcji zawierającego instrukcję throw i
wyszukanie instrukcji try, której fraza catch przechwyci zgłoszony
wyjątek. Jeżeli nie ma takiej instrukcji try, zostanie wywołana metoda
UncaughtException i wykonanie programu (lub wątku) zostanie
zakończone.
• Fraza:throws klasa_wyjątków może wystąpić w nagłówku funkcji
(metodzie wystąpienia, konstruktorze, funkcji klasy)
• Blok frazy finally (jeśli występuje) jest wykonywany zawsze gdy
kończy się wykonanie instrukcji try i to nawet wtedy, gdy wykonanie
try zostaje gwałtownie przerwane.
• Zobaczmy przykład:
Współbieżność w języku Java
• „Obiekty zapewniają sposób podziału programu
na niezależne części. Często jednak musimy
również podzielić program na rozłączne i
niezależnie działające podzadania”
• Każde z niezależnych zadań nazywa się wątkiem
(ang. Thread)
• Z punktu widzenia programisty każdy wątek
działa samodzielnie posiadając dokładnie jeden
procesor dla siebie
Proces a wątek
• Proces jest wykonującym się programem z
własną przestrzenią adresową (przydzieloną
mu pamięcią)
• Wątek jest osobnym, pojedynczym
sekwencyjnym przepływem sterowania
działającym w ramach jednego procesu
Cechy programowania
współbieżnego
1. Wymaga innego sposobu myślenia o
programowaniu niż klasyczne
programowanie strukturalne czy
obiektowe
2. W każdym języku programowania
programowanie współbieżne jest podobne
i wymaga podobnego sposobu myślenia
Wątki w Java
• Na potrzeby wykładu poznamy jedynie
podstawową zasadę działania mechanizmu
wątków
• Dogłębna analiza wymaga zagłębienia się w
filozofię i szczegółowe rozwiązania języka
Po co stosować wątki?
•
•
•
•
Interfejsy
Obsługa urządzeń sieciowych
Obsługa urządzeń elektronicznych
Optymalizacja zadań
Jak to się pisze?
• Poniższy przykład ilustruje jak się w Java
tworzy wątki
• Klasa java.lang.Thread
• Implementacja interfejsu Runnable
• Metoda run()
• Spójrzmy na przykład:
Wynik działania programu
Wyjście z programu będzie mieć postać zależną od maszyny (wątki mogą się przeplatać):
Name of x: Thread-0
Priority of x= 5
Name of y: Thread-1
Priority of y= 5
End of main()
Msg 1 from Thread-0
Msg 2 from Thread-0
Msg 3 from Thread-0
Msg 1 from Thread-1
Msg 4 from Thread-0
Msg 2 from Thread-1
Thread-0 DONE!!
Msg 3 from Thread-1
Msg 4 from Thread-1
Thread-1 DONE!!
Uwaga. Nie należy wywoływać jawnie metody run().
Bezpośrednie wywołanie run() spowoduje jej wykonanie w
normalny, sekwencyjny sposób
Cechy wątków
• Priorytet wątków (liczby od 1-10) oznacza
„ważność” wątków. JVM w zależności od
priorytetu wątku przydziela odpowiednio (z
określonym prawdopodobieństwem) czas
procesora.
setPriority(int priority);
• Dwa osobne wątki działają w sumie wolniej
niż jeden wątek główny.
Wielowątkowość
• Jeżeli maszyna jest wieloprocesorowa wątki mogą
być naprawdę wykonywane współbieżnie, w
przeciwnym wypadku mamy do czynienia z
wielowątkowością wywłaszczeniową.
• Wielowątkowość wywłaszczeniowa polega na
rozdzielaniu poszczególnym wątkom kwantów
procesora (w zależności od ich priorytetu)
• Wątki nie są „karmione” procesorem „jeden po
drugim”, lecz na przemian.
Terminy związane z
wielowątkowością
• Terminy związane:
–
–
–
–
Zagłodzenie wątku
Łączenie wątków
Wątki demony
Uśpienie wątków
Zagłodzenie wątku
• Uwaga. Zagłodzenie może się zdarzyć wtedy, gdy
jeden lub więcej wątków w programie jest
blokowanych przed dostępem do pewnego zasobu i
wskutek tego nie mogą biec dalej. Krańcową
postacią zagłodzenia jest zakleszczenie lub impas
(deadlock). Impas pojawia się wtedy, gdy dwa lub
więcej wątków czeka na warunek, który nie może
być spełniony; typowym przykładem jest sytuacja,
gdy istnieją dwa wątki i każdy z nich czeka na
wykonanie czegoś przez partnera
Interfejs Runnable
• Mówiliśmy już wcześniej, że wątki można definiować na
dwa sposoby
– Dziedziczenie po klasie java.lang.Thread
– Implementacja interfejsu Runnable
• Właściwie oba te rozwiązania oznaczają to samo, gdyż
klasa Thread implementuje interfejs Runnable, a klasa
dziedzicząca po niej też go musi definiować.
• Różnice są tylko w sposobie deklarowania wątku, czyli z
punktu programisty. Z koncepcyjnego punktu widzenia
różnic nie ma.
• Spójrzmy na przykład:
Współpraca wątków
• Jeden, pojedynczy program można
traktować jako samotnika poruszającego się
dowolnie po przestrzeni zasobów (pamięci,
portów, urządzeń zewnętrznych i innych)
• W takim przypadku nie ma problemów z
dostępem do zasobów, które mogą naraz
obsługiwać jednego użytkownika
(urządzenia sieciowe, pliki i inne)
Istota problemu
• W programie wielowątkowym może się
zdarzyć sytuacja, że dwa wątki naraz chcą
korzystać z tego samego zasobu (plik, baza
danych, zasób sieciowy itp).
• Rozwiązanie:
Synchronizacja wątków
Sekcja krytyczna
• Segmenty kodu programu, które żądają
dostępu do tego samego obiektu z dwóch
oddzielnych, współbieżnych wątków,
nazywa się sekcjami krytycznymi. W języku
Java sekcją krytyczną może być blok lub
metoda; identyfikuje się je słowem
kluczowym synchronized.
synchronized
• Instrukcja synchronized przejmuje wzajemnie
wykluczającą blokadę na rzecz wykonywanego
wątku, wykonuje Blok, po czym zwalnia blokadę.
Inaczej mówiąc, wykonanie instrukcji
synchronized powoduje przydzielenie wątkowi
podanego Bloku jako sekcji krytycznej, a po
wykonaniu go na rzecz obiektu identyfikowanego
przez Wyrażenie, zwolnienie sekcji.
• Spójrzmy na prosty przykład:
Synchronizowanie metod
• Jeżeli różne metody korzystają z tego samego
zasobu, to również muszą być synchronizowane.
• Metoda jest synchronizowana jeżeli w jej
nagłówku umieszczono słowo kluczowe
synchronized.
• Synchronizowana metoda operująca na obiekcie
pewnej klasy automatycznie nakłada blokadę na
ten obiekt przed wykonaniem jego ciała (funkcji,
metod) i automatycznie zwalnia blokadę przy
powrocie, podobnie jak instrukcja synchronized.
• Zauważmy, że w klasie Test metoda classBump()
jest statyczna (jest metodą klasy). Zatem, mimo iż
metoda ta jest synchronizowana, może być
jednocześnie wywoływana na rzecz wielu
obiektów, a więc blokada nie będzie efektywna.
Dla uniknięcia możliwości jednoczesnego
wykonywania pewnej operacji na tym samym
obiekcie (w szczególności zawierającym metody
statyczne) przez dwa różne wątki dobrą praktyką
jest definiowanie klas tak, aby były przygotowane
na użycie współbieżne
Synchronizacja metod
Każde wystąpienie klasy Box ma pewną zawartość
zmiennej wystąpienia, która utrzymuje referencję do
dowolnego obiektu. W rezultacie można włożyć obiekt
do pudełka (Box) wywołaniem metody put(), która
zwróci false jeżeli pudełko jest już pełne. Podobnie
można wyjąć obiekt z pudełka wywołaniem metody
get(), która zwróci null jeżeli pudełko jest puste. Gdyby
put() i get() nie były synchronizowane i dwa wątki
wykonywałyby te metody na tym samym obiekcie klasy
Box w tym samym czasie, wtedy wykonanie programu
dwuwątkowego mogłoby przebiegać w sposób przez nas
nieoczekiwany
Download