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