Tworzenie obiektów
◮
Dostęp do obiektów jest realizowany przez referencje.
◮
Obiekty w języku Java są tworzone poprzez użycie słowa
kluczowego new. String lan = new String(”Lancuch”);
◮
Obszary pamięci w których przechowywane są dane:
Stos
zmienne automatyczne, referencje do
obiektów (ale nie obiekty).
Sterta
wszystkie obiekty utworzone w trakcie ży-
cia programu,
Obszar statyczny
dane dostępne w dowolnym momencie
podczas pracy programu (wprowadzone
słowem kluczowym static),
Obszar poza RAM
obiekty przechowywane w postaci stru-
mieni bajtów poza pamięcią RAM.
Opakowywanie (uobiektowianie) typów pierwotnych
◮
Zmienne tworzone w oparciu o typy pierwotne, tworzone są
jako zmienne automatyczne na stosie:
int i = 1;
◮
czasem zachodzi potrzeba utworzenia dynamicznej zmiennej
przechowującej wartość typu pierwotnego ...
◮
czasem korzystne jest by wartość typu pierwotnego traktowana
była jak obiekt ...
◮
... wtedy używa się klasy „opakowującej”
Integer i = new Integer(1);
Opakowywanie (uobiektowianie) typów pierwotnych
klasy opakowujące
Pierworne typy danych oraz odpowiadające im klasy „opakowujące”:
char
Character
byte
Byte
short
Short
int
Integer
long
Long
float
Float
double
Double
boolean
Boolean
void
Void
Zasięg zmiennych, czas życia obiektów
Zasięg zmiennych okreslony jest tak samo jak w C++. Czas życia
obiektów jest inny.
◮
Zasięg zmiennych automatycznych jest określany nawiasami :
{
int i = 1
} // koniec zasięgu (zmienna i przestaje istnieć)
◮
Obiekty są dostępne poza zasięgiem, w którym aktualne są
referencje:
{
String s = new String(”Lancuch”);
} // koniec zasięgu zmiennej s
// ale obiekt wskazywany przez s nadal istnieje!
◮
Obiekty do których nie ma dostępu ze względu na to, że wszystkie
referencje wskazujące na ten obiekt znajdują się poza zasięgiem, są
usuwane przez garbage collector (w C++ istnieją bezużytecznie do
końca działania programu stanowiąc tzw. ’wyciek pamięci’)
Operator przypisania
◮
Przypisanie realizowane jest przy pomocy operatora =
◮
Podczas przypisywania jednemu obiektowi innego obiektu
kopiowana jest referencja. Po zrealizowaniu takiej operacji
zmienne wskazują na ten sam obiekt (obszar pamięci). Obiekt
nie jest kopiowany!
◮
patrz przykład Przypisanie
Operatory porównania
◮
operatory == oraz != działają dla wszystkich typów wartości
◮
należy wszakże pamietać, że porównując ’wartości obiektowe’,
porównujemy zawsze referencje, nie obiekty
◮
operator == zwraca wartość ’prawda’ jeśli po obu stronach
stoi ten sam obiekt, nie taki sam obiekt
◮
porównanie obiektów realizuje się za pomocą metody equals
◮
dla własnych obiektów metodę tę trzeba zdefiniować samemu
◮
patrz przykłady Porównanie_Integer, Porównanie_Para
Tworzenie klas
◮
klasy tworzone są przy użyciu słowa kluczowego class
◮
każda klasa składa się z danych składowych (atrybuty) oraz
funkcji składowych (metody)
◮
dostęp do składowych obiektów odbywa się przy pomocy
operatora . (kropka)
◮
każda z klas utrzymuje własny obszar pamięci dla swoich
atrybutów
◮
atrybuty nie zainicjowane otrzymują zawsze wartości domyślne:
◮
boolean: false
◮
pozostałe typy: 0
Metody
◮
metody w języku Java mogą być tworzone jedynie jako części
składowe klasy
◮
wywołanie metody dla obiektu czasem nazywa się wysyłaniem
komunikatu do obiektu (za Smalltalkiem)
◮
metody mogą posiadać argumenty oraz zwracać wartości
◮
jeżeli argumentami są obiekty do ciała funkcji przekazywane są
ich referencje
◮
jeżeli argumentami są wartości typów pierwotnych
przekazywane są ich kopie
◮
słowo kluczowe return oznacza miejsce zakończenia działania
metody oraz wskazuje zwracaną wartość
Składowe statyczne
◮
słowo kluczowe static
◮
atrybuty statyczne przechowywane są w jednym obszarze
pamięci wspólnym dla całej klasy (obiektów)
◮
metody statyczne wywoływane mogą być bez konieczności
tworzenia obiektów klasy
class KlasaStatyczna {
static int i = 1;
static void metodaStatyczna() { i++; }
}
◮
atrybuty statyczne nazywane są czasem atrybutami (polami)
klasy, odnoszą się do klasy
◮
możliwe są dwa sposoby odwoływanie się składowych
statycznych
◮
przez obiekt klasy: o.metodaStatyczna(...) (o - obiekt klasy
KlasaStatyczna)
◮
przez nazwę klasy: KlasaStatyczna.metodaStatyczna (w C++
uzywany jest tu oprator zakresu)
Metody statyczne
◮
metody statyczne nie operują na obiektach, nie mają dostępu
do atrybutów obiektów
◮
metody statyczne są wykorzystywane w celu realizowania
dostępu do zmiennych statycznych danej klasy
◮
jednym z podstawowych zastosowań metody statycznej jest
możliwość wywoływania metody main() bez konieczności
tworzenia obiektu rozpoczynającego działanie programu
Atrybuty statyczne
◮
jeżeli atrybut statyczny jest typu podstawowego i nie zostanie
mu nadana wartość początkowa to zyska domyślną wartość
początkową właściwą dla jej typu
◮
atrybuty statyczne są inicjalizowane podczas pierwszego
odwołania do klasy (tworzenia obiektu klasy, odwołania do
składowej statycznej)
◮
Możliwe jest zbieranie wszystkich inicjalizacji statycznych w
bloki rozpoczynające się od słowa kluczowego static.
class Przykład {
static int i,j;
static {
i = 1;
j = 2;
}
}
Przeciążanie metod
W języku Java dozwolone jest przeciążanie metod (wprowadzanie
metod o tych samych nazwach). Metody przeciążone są
rozróżnione przez kompilator poprzez ich argumenty.
Parametry metod
◮
parametry w języku Java są zawsze przekazywane przez
wartość (jak w C!) – metoda otrzymuje kopię wszystkich
wartości parametrów
◮
powyższe dotyczy wszystkich typów pierwotnych oraz referencji
◮
w przypadku, w którym parametrem jest referencja do obiektu,
możliwe jest realizowanie przez daną metodę operacji na
wskazywanym obiekcie: do metody przekazywana jest kopia
referencji, która wskazuje na ten sam obiekt co oryginał!
Inicjalizacja obiektu
◮
inicjalizacja obiektu realizowana jest przez konstruktor, czyli
metodę automatycznie wywoływaną podczas tworzenia obiektu
◮
nazwa konstruktora jest taka sama jak nazwa klasy
◮
podobnie jak inne metody konstruktory mogą posiadać
argumenty
◮
jedna klasa może posiadać wiele konstruktorów – w tej sytuacji
ma miejsce przeładowanie konstruktora
◮
konstruktor nie zwraca wartości
◮
jeżeli w sposób jawny nie zostanie zdefiniowany żaden
konstruktor kompilator automatycznie utworzy konstruktor
domyślny (konstruktor bezargumentowy) .
◮
jeżeli jawnie zdefiniujemy konstruktor, konstruktor domyślny
nie jest automatycznie tworzony przez kompilator.
Inicjalizacja atrybutow (niestatycznych)
◮
atrybuty mogą być inicjalizowane:
◮
w konstruktorze
◮
poprzez przypisanie wartości przy deklaracji atrybutu
◮
w bloku inicjalizacji
◮
blok inicjalizacji wykonywany jest przed wywołaniem
konstruktora
class Osoba {
public Osoba(){
...
}
// Blok inicjalizacji
{
nazwisko = ””;
....
}
private String nazwisko;
...
this
◮
słowo kluczowe this pozwala na odwołanie się do obiektu, na
którego rzecz jest wywoływana
◮
this
powinno być wykorzystywane w tych przypadkach w których
zachodzi potrzeba jawnego odwoływania się do aktualnego obiektu
np. w sytuacji zwracania odwołania do aktualnego obiektu w
instrukcji return.
public class This {
This zwieksz() {
i++;
return this;
}
void wypisz() {
System.out.println("i = " + i);
}
public static void main(String[] args) {
This x = new This();
x.zwieksz().zwieksz().zwieksz().wypisz();
}
int i = 0;
}
Metoda finalize
◮
java nie posiada destruktorów, aby więc zwalniać
wykorzystywane zasoby wprowadzona została metoda
finalize
◮
metoda finalize zostanie wywołana zanim mechanizm garbage
collector zniszczy dany obiekt
◮
Uwaga: poleganie na metodzie finalize może być bardzo
zawodne – nie jest możliwe określenie kiedy i czy zostanie
wywołany garbage collector a w konsekwencji kiedy i czy
zostanie wywołana metoda finalize
◮
W celu realizowania operacji kończących działanie danego
obiektu zaleca się wprowadzanie metod kończących (zakoncz,
dispose) oraz ręczne ich wywoływanie.
Widoczność składowych
modyfikatory dostępu do składowych klasy
◮
w języku Java możliwe jest korzystanie z modyfikatorów
dostępu: public, protected, private.
◮
modyfikatory dostępu powinny zostać umieszczone w sposób
jawny przed każdą składową klasy (atrybut, metoda)
◮
w przypadku braku użycia modyfikatora dostępu przyjmowany
jest tzw. „przyjazny” dostęp do składowych tzn. inne klasy
tego samego pakietu uzyskują dostęp do danego elementu, dla
klas spoza pakietu element ten jest prywatny
◮
dzięki dostępowi „przyjaznemu” możliwe jest grupowanie
spokrewnionych klas w pakiety umożliwiające łatwą
komunikację pomiędzy klasami
◮
występowanie dostępu „przyjaznego” wymaga od programisty
dbałości o grupowanie klas w pakiety.
Widoczność składowych
◮
modyfikator public udostępnia daną składową dla wszystkich
klas korzystających z danej klasy
◮
modyfikator protected udostępnia składową wszystkim klasom
pochodnym (znajdującym się niżej w hierarchii dziedziczenia)
◮
modyfikator private oznacza, że dana składowa jest
udostępniana jedynie z wnętrza innych metod danej klasy
◮
zalecenia:
◮
dostęp „przyjazny” jest w większość przypadków
wystarczającym sposobem ukrywania składowych
◮
dodczas projektowania typowym problemem powinny być
określenie które metody powinny być publiczne, a nie prywatne
Widoczność klasy
modyfikator dostępu dla klasy
W języku Java możliwe jest określenie dostępności dla klas.
◮
dostęp publiczny: modyfikator public, klasa jest dostępna dla
wszystkich
◮
dostęp przyjazny: brak modyfikatora, klasa jest dostępna
jedynie wewnątrz pakietu
◮
nie jest dopuszczalne używanie modyfikatorów private i
protected
Organizowanie programów
◮
kod źródłowy zapisywany jest w jednostkach kompilacji czyli
plikach z rozszerzeniem .java
◮
w danej jednostce kompilacji może znajdować się co najwyżej
jedna klasa publiczna
◮
jeżeli w pliku znajduje się definicja klasy publicznej, to nazwa
pliku musi mieć postać nazwaklasypublicznej.java
◮
po kompilacji dla każdej klasy z pliku .java tworzone są pliki
wynikowe z rozszerzeniem .class
◮
działający program to zbiór plików .class; interpreter Javy
odpowiada za znajdowanie i interpretowanie tych plików
Pakiety
tworzenie pakietu
◮
pakiety to mechanizm grupowania klas w biblioteki
◮
wskazanie, że dana jednostka kompilacji należy do pakietu jest
realizowane przez umieszczenie na początku pliku deklaracji
package
z nazwą pakietu:
package mojpakiet;
Pakiety
wykorzystanie (klasy z) pakietu
◮
wskazanie kompilatorowi/interpreterowi skąd pobrać potrzebne
klasy jest realizowane przy pomocy słowa kluczowego import
import mojpakiet.KlasaPrzykladowa
// import klasy KlasaPrzykladowa z pakietu mojpakiet
import mojpakiet.*;
// import wszystkich klas z pakietu mojpakiet
◮
odwołanie do ’obcej’ klasy możliwe jest również poprzez użycie
nazwy z pełnym kwalifikatorem (wtedy nie jest potrzebny
import
):
mojpakiet.KlasaPrzykladowa kp = new mojpakiet.KlasaPrzykladowa()
◮
nazwa z pełnym kwalifikatorem powinna zostać również użyta
w przypadku gdy w dwóch bibliotekach znajdują sie klasy o tej
samej nazwie
Pakiety
Jak interpreter/kompilator znajduje pakiety/klasy
◮
zmienna środowiskowa CLASSPATH zawiera listę ścieżek
poszukiwań dla klas Javy, np.
CLASSPATH=.:/home/to/lib/java
◮
jeżeli klasa jest elementem pakietu, to odpowiadający jej plik
musi znajdować się w katalogu o tej samej nazwie, co nazwa
pakietu; nazwa ta dodawana jest do ścieżek poszukiwań jako
podkatalog:
import mojpakiet.KlasaPrzykladowa
// szukany plik mojpakiet/KlasaPrzykladowa.class
// lub
/home/to/lib/java/mojpakiet/KlasaPrzykladowa.class
◮
jeżeli nazwa pakietu zawiera kropki, są one zamieniane na znak
/
lub \, w zależności od systemu operacyjnego
import narzedzia.interfejsy.mojpakiet.KlasaPrzykladowa
// szukany plik narzedzia/interfejsy/mojpakiet/KlasaPrzykladowa.class
// lub
/home/to/lib/java/narzedzia/interfejsy/mojpakiet/KlasaPrzykladowa.c
◮
w ten sposób biblioteki klas organizuje się w hierarchie