Java niezbędnik programisty spotkanie nr 3 Modyfikatory, jednostki kompilacji, tworzenie/inicjalizacja, odśmiecanie/ finalizacja... 1
Definicja klasy [modyfikator] class nazwa_klasy { [modyfikator] nazwa_typu [=wyrażenie_inicjalizujące]; [modyfikator] typ_zwr nazwa_met([arg]) [klauzula_throw] { kod_metody 2
Modyfikatory Modyfikatory dostępu: public, protected, (domyślny dostęp pakietowy), private (słowo default ma inne zastosowanie) Modyfikatory dostępu używamy z klasami, interfejsami, atrybutami, metodami, konstruktorami i klasami wewnętrznymi. Nie wszystkie modyfikatory stosują się do każdego zagadnienia, np. klas dotyczą jedynie public i (domyślny dostęp pakietowy) Pozostałe modyfikatory: abstract (klasa, metoda), final (klasa, metoda, zmienna), native (metoda), static (metoda, atrybut), synchronized (metoda), volatile (zmienna), transient (atrybut), strictfp (klasa, metoda) 3
Modyfikatory - przykład package niezbednik3; import jakies.klasy.*; //kolejność definicji klas jest nieistotna public class Test { public static void main(string[] args) { Modyfikatory modyf = new Modyfikatory(); modyf.a = 1; modyf.b = 2; modyf.c = 3; //modyf.d = 4; //klasy mogą mieć jedynie dostęp pakietowy lub public class Modyfikatory { public int a; protected int b; int c; private int d; 4
Modyfikatory - przykład package niezbednik3; import jakies.klasy.*; public class Test { public static void main(string[] args) { inny.pakiet.modyfikatory modyf; modyf = new inny.pakiet.modyfikatory(); modyf.a = 1; //modyf.b = 2; //modyf.c = 3; //modyf.d = 4; ------------------------------------------------------------- package inny.pakiet; public class Modyfikatory { public int a; protected int b; int c; private int d; 5
Kontrola jest w chwili kompilacji Modyfikatory ograniczają dostęp na poziomie kodu. To nie jest mechanizm kontroli uprawnień na poziomie obiektów. class SłużbySpecjalne { private String nazwiskoagenta; protected String inwigiluj(służbyspecjalne ss) { return ss.nazwiskoagenta; System.out.println(ssKraj2.inwigiluj(ssKraj1)); Dzięki temu kontrolę może przeprowadzić kompilator! Jak to nie wystarcza, to są języki aspektowe. 6
Jednostki kompilacji Programy w Javie składają się z jednostek kompilacji plików z kodem źródłowym. Pliki z kodem źródłowym mają rozszerzenie.java Każdy plik z kodem źródłowym może zawierać co najwyżej jedną publiczną klasę lub interfejs. Jeżeli zawiera - plik musi się nazywać tak samo jak klasa/interfejs. Jeżeli nie zawiera - może się nazywać dowolnie. 7
import Pomiędzy deklaracją pakietu i deklaracjami klas i interfejsów możemy wskazać jakich klas i interfejsów chcemy używać bez kwalifikowania ich nazw nazwą pakietu: import pak1.pak2.klasa; import pak1.interfejs; import pak1.*; //nie obejmuje podpakietów //java.lang.* jest importowane domyślnie JVM ładuje potrzebne klasy w chwili pierwszego odwołania, a odnajduje je dzięki wartości CLASSPATH (zmienna środowiska lub parametr wywołania). Jeżeli CLASSPATH=/java:. to poszukiwany będzie plik /java/pak1/pak2/klasa.class, jeżeli taki nie istniej to./pak1/pak2/klasa.class. W CLASSPATH można też wskazać archiwum (jar lub zip). Nie znalezienie importowanej klasy/interfejsu powoduje błąd podczas kompilacji. 8
import c.d. Jeżeli pakiety pak1 i pak2 zawierają klasę A to: import pak1.a; import pak2.a; spowoduje błąd podczas kompilacji import pak1.*; import pak2.a; będzie poprawne i używana będzie A z pak2, chyba że A jest również definiowana w aktualnym pakiecie import pak1.*; import pak2.*; będzie poprawne, o ile nigdzie nie próbujemy odwoływać się do A i nie definiujemy tej klasy w aktualnym pakiecie 9
Przeciążanie metod Metody odróżniamy na podstawie nazwy i listy parametrów Nazwy jakie nadamy parametrom nie mają znaczenia, liczą się jedynie ich typy public void mojametoda(string jakaśnazwa) public void mojametoda(string innanazwa) Czasami metody wykonujemy jedynie dla ich efektów ubocznych, a zwracana wartość nas nie interesuje (więc nie służy do odróżniania metod przeciążonych) space.write(entry, transaction, lease); dodaj(4, 5); //4+5; //nie skompiluje się 10
Przeciążanie metod - przykład 1 class PrzeciazanieMetod { void f(char x) {System.out.println("char"); void f(byte x) {System.out.println("byte"); void f(short x) {System.out.println("short"); void f(int x) {System.out.println("int"); void f(long x) {System.out.println("long"); void f(float x) {System.out.println("float"); void f(double x) {System.out.println("double"); //void f(object o) {System.out.println("Object"); //void f(string s) {System.out.println("String"); char jest promowany do int (do short nie) dla obiektów wybierany jest typ bardziej szczegółowy 11
Przeciążanie metod - przykład 2 class PrzeciazanieMetod { void f(short b, int i) {System.out.println("short, int"); void f(int b, short i) {System.out.println("int, short"); //pm.f(1, 1); //żadna metoda nie pasuje //pm.f((short) 1, (short) 1); //pasują obie pm.f(1, (short) 1); //int, short pm.f((char) 1, (byte) 1); //int, short 12
Tworzenie/inicjalizacja obiektów Użytkownicy naszych bibliotek nie zawsze będą świadomi, że obiekty należy zainicjalizować. Inicjalizacja jest tak ważna, że warto mieć specjalną metodę - konstruktor, która jest automatycznie wywoływana zaraz po stworzeniu obiektu. Konstruktory mają taką samą nazwę jak klasa (pierwsza litera jest wielka). Konstruktory można przeciążać. Ponieważ konstruktor jest wywoływany w chwili użycia operatora new, to nie ma sensu, żeby miał wartość zwrotną. Każda klasa musi mieć konstruktor, jeżeli żadnego nie zdefiniowaliśmy kompilator doda za nas domyślny, pusty, bezparametrowy. Pierwszą operacją w konstruktorze może być wywołanie innego konstruktora (ale nie mogą powstać cykle).
Tworzenie/inicjalizacja obiektów package niezbednik3; import java.util.*; class TestKonstruktora { Date d; TestKonstruktora() { d = new Date(); class TestKonstruktora2 { Date d; //domyślny bezparametrowy konstruktor jest dodawany jedynie //jeżeli sami żadnego nie zdefiniowaliśmy TestKonstruktora tk = new TestKonstruktora(); TestKonstruktora2 tk2 = new TestKonstruktora2(); 14
Tworzenie/inicjalizacja obiektów class TestKonstruktora3 { Date d; TestKonstruktora3(Date d) { this.d = new Date(); //bezparametrowe konstruktory są ważne jak przekazujemy //nasze klasy do ogólnych bibliotek TestKonstruktora3() { this(new Date()); //wywołanie innego konstruktora... 15
Zwalnianie zasobów W Javie nie ma żadnych jawnych bądź niejawnych destruktorów. Zasoby inne niż pamięć przyjęło się zwalniać w metodzie close() (trzeba ją samemu definiować i wywoływać) plik.close(); gniazdo.close(); połączeniezbazą.close(); Nie musimy/możemy się martwić o zwalnianie pamięci, w której przechowywane są Javowe obiekty (sterta) i zmienne (stos), bo sami jej nie przydzielamy. O obiektach, których już nie będziemy potrzebować można zwyczajnie zapomnieć: { String s = "ala"; System.out.println(s); 16
Odśmiecanie/Finalizacja Zwalnianiem pamięci po obiektach zajmuje się odśmiecacz. Odśmiecenie może nastąpić jedynie, jeżeli dany obiekt nie jest dostępny z żadnego aktywnego wątku programu. Za jego uruchamianie i stosowane algorytmy odpowiada maszyna wirtualna. Użytkownik może jej jedynie "podpowiedzieć, że warto podjąć starania w celu odśmiecenia pamięci": System.gc(). Jeżeli pamięci jest dużo, odśmiecanie może nie być potrzebne. Przed odśmieceniem danego obiektu wywoływana jest odziedziczona po Object metoda finallize(). O finallize() wiadomo, że będzie wywołana co najwyżej raz, dlatego nie ma sensu zwalniać w niej jakichkolwiek zasobów. Można natomiast sprawić, że obiekt stanienie się ponownie dostępny. Jako ćwiczenie proszę sprawdzić, czy przed odśmieceniem (po raz drugi) przywróconego obiektu finallize() będzie wywołana drugi raz. 17
Finalizacja - Przykład class Finalizacja { int n; String[] smieci; Finalizacja(int n) { this.n = n; smieci = new String[n]; smieci[0] = "Każda literka zaśmieca dwa bajty."; for (int i=1; i<n; i++) smieci[i] = smieci[i-1] + smieci[i-1]; protected void finalize() throws Throwable { super.finalize(); System.out.println("finalize() dla n=" + n); 18
Finalizacja Przykład c.d. Wywołanie: for (int i=1; i <= 13; i++) { Finalizacja f = new Finalizacja(i); Może dać następujący wynik: finalize() dla n=9 finalize() dla n=10 finalize() dla n=12 19
Jak działa odśmiecacz Odnajdywanie niedostępnych obiektów: zliczanie referencji (znajdywanie grup obiektów odwołujących się do siebie na wzajem jest kosztowne) przeglądanie sieci dostępnych (przez stos) obiektów i jakoś ich oznaczanie Porządkowanie sterty: stop-and-copy - odśmiecamy i porządkujemy (przesuwamy obiekty dbając o naprawianie referencji); strategia dobra jak dużo odśmiecania mark-and-sweep jedynie odśmiecamy; strategia dobra jak mało odśmiecania stop-and-copy można udoskonalić dzieląc stertę na bloki i umieszczając w każdym licznik jego wykorzystania. Maszyna wirtualna może przełączać strategie w zależności od własności działającego programu. 20
Inicjalizacja c.d. class Pom1 { Pom1() {System.out.println("Pom1()"); class TestInicjalizacji { int x = 777; Pom1 p = new Pom1(); int y = f(); int f() { System.out.println("f()"); return 1; //deklaracje metod mogą się przeplatać z deklaracjami atrybutów int yy; TestInicjalizacji() { System.out.println("TestInicjalizacji()");... kolejność: Pom1(), f(), TestInicjalizacji() 21
Inicjalizacja c.d.... //kolejność deklaracji zmiennych ma znaczenie przy inicjalizacji int v = x; //int a = b; int b; //kolejność jest też istotna przy przekazywaniu parametrów //int a = b(u); int b(int u) { return u; int u;... 22
Inicjalizacja c.d.... //można jednak oszukiwać int z = h(); int h() { System.out.println("h(); u="+u); return u; int u = 1313; //to by się skompilowało!!! //ale zawsze dojdzie do przepełnienia stosu (StackOverflowError) //TestInicjalizacji ti = new TestInicjalizacji();... wynik: h(); u=0 23
Inicjalizacja statyczna Inicjalizacja składowych statycznych odbywa się jeden raz, w momencie pierwszego użycia klasy (odwołanie do składowej statycznej lub utworzenie obiektu). class InicjalizacjaStatyczna { static Pom1 p = new Pom1(); static void test() {System.out.println("test()"); InicjalizacjaStatyczna.test(); InicjalizacjaStatyczna is = new InicjalizacjaStatyczna(); Class c = InicjalizacjaStatyczna.class; 24
Grupowanie inicjalizacji class BlokInicjalizacji { int x; //o wyjątkach jeszcze opowiemy { try { System.out.println("ustawiam x"); x = 7; catch (Exception e) { System.out.println("Wpadłeś jak śliwka w studzienkę kanalizacyjną"); static int y; static { int pom; pom = 1; System.out.println("ustawiam y"); y = pom; Kolejność od góry na dół. Inicjalizacja statyczna i niestatyczna będzie poprzeplatana. 25