Notatki z Javy (SCJP), cz. 2

    -- Sebastian Pawlak, 2006.


Math

Pakiet java.lang definiuje fundamentalne dla Javy klasy, z tego względu jest on
importowany automatycznie. 
Pakiet definiuje wrappery dla typów prymitywnych: Boolean, Byte, Character, Double, 
Float, Integer, Long, Short, Void, Object. 
java.lang zawiera klasę Math, która jest używana podczas wykonywania podstawowych
operacji matematycznych. 
Klasa Math definiuje m.in.: 
public final static double Math.PI 
public final static double Math.E 
 
Wszystkie metody klasy Math są statyczne, zatem nie trzeba tworzyć obiektu, aby je wywołać. 
Nawet gdyby chcieć stworzyć instancję klasy Math, jest to niemożliwe, ponieważ konstruktor
jest prywatny. 
Nie można też dziedziczyć po klasie Math, ponieważ jest ona "final". 
 
 
abs() 
 
            x = Math.abs(99);    // 99 
            x = Math.abs(-99);   // 99 
 
            abs() może zwrócić wartość ujemną dla minimalnej wartości: 
            System.out.println(Math.abs(Integer.MIN_VALUE));    // -2147483648 
 
            public static int abs(int a); 
            public static long abs(long a); 
            public static float abs(float a); 
            public static double abs(double a); 
 
 
ceil()      - zaokrąglanie w górę (sufit) 
 
            Math.ceil(9.0);    // 9.0 
            Math.ceil(8.8);    // 9.0 
            Math.ceil(8.02);   // 9.0 
 
            Math.ceil(-9.0);    // -9.0 
            Math.ceil(-9.4);    // -9.0 
            Math.ceil(-9.8);    // -9.0 
 
            public static double ceil(double a); 
 
            Działa tylko dla double - nie jest przeciążona. 
 
 
floor()     - zaokrąglanie w dół (podłoga) 
 
            Math.floor(9.0);    // 9.0 
            Math.floor(9.4);    // 9.0 
            Math.floor(9.8);    // 9.0 
 
            Math.floor(-9.0);    // -9.0 
            Math.floor(-8.8);    // -9.0 
            Math.floor(-8.1);    // -9.0 
 
            public static double floor(double a); 
 
            Działa tylko dla double - nie jest przeciążona. 
 
 
max() 
 
            x = Math.max(1024, -5000);    // 1024 
 
            public static int max(int a, int b); 
            public static long(long a, long b); 
            public static float max(float a, float b); 
            public static double max(double a, double b); 
 
 
min() 
 
            x = Math.min(0.5, 0.0);    // 0.0 
 
            public static int min(int a, int b); 
            public static long min(long a, long b); 
            public static float min(float a, float b); 
            public static double min(double a, double b); 
 
---- 
 
 
public class Main { 
    public static void main(String[] args) { 
        double[] tab = { 10.5, -10.5, Math.PI, 0 }; 
         
        for (int i = 0; i < tab.length; i++) 
	    System.out.println(Math.abs(tab[i]) + "  " + Math.ceil(tab[i]) + " " +
                               Math.floor(tab[i])); 
    } 
} 
// 10.5    11.0     10.0 
// 10.5    -10.0    -11.0 
// 3.14    4.0      3.0 
// 0.0     0.0      0.0 
 
---- 
 
random()    -- zwraca losową wartość  0.0 <= x < 1.0 
               nie pobiera żadnych argumentów 
 
            public static double random(); 
 
 
round() 
 
            Math.round(-10.5);    // -10 
 
            Math.round(10.5);     // 11 
            Math.round(10.1);     // 10 
            Math.round(10.7);     // 11 
 
            public static int round(float a); 
            public static long round(double a); 
 
 
sin() 
 
            Math.sin(Math.toRadians(90.0));    // 1.0 
 
            public static double sin(double a); 
 
 
cos() 
 
            Math.cos(Math.toRadians(0.0));    // 1.0 
 
            public static double cos(double a); 
 
 
tan() 
 
            Math.tan(Math.toRadians(45.0));    // 1.0 
 
            public static double tan(double a); 
 
 
sqrt() 
 
            Math.sqrt(9.0);    // 3.0 
 
            Math.sqrt(-9.0);   // NaN      - Not a Number 
 
            public static double sqrt(double a); 
 
 
toDegrees()  -- radiany na kąt w stopniach 
 
            Math.toDegrees(Math.PI * 2.0);    // 360.0 
 
            public static double toDegrees(double a); 
 
 
toRadians()  -- kąt w stopniach na radiany 
 
            Math.toRadians(360.0);    // 6.283185, czyli 2 * Math.PI 
 
            public static double toRadians(double a); 
 
 
---- 
 
double d; 
float pi_i = Float.POSITIVE_INFINITY; 
double n_i = Double.NEGATIVE_INFINITY; 
double notanum = Double.NaN; 
 
System.out.println(Double.POSITIVE_INFINITY + " " +
                   Double.MAX_VALUE);  // Infinity 1.7976931348623157E308 
 
if (notanum != notanum)                       // NaN nie jest równy niczemu, nawet samemu sobie ! 
    System.out.println("NaNs not equal"); 
 
if (Double.isNaN(notanum)) 
    System.out.println("got a NaN"); 
 
d = Math.sqrt(n_i); 
if (Double.isNaN(d)) 
    System.out.println("got sqrt NaN");       // got sqrt NaN 
 
System.out.println(Math.sqrt(-16d));          // NaN 
 
System.out.println(16d / 0.0);                // Infinity 
System.out.println(16d / -0.0);               // -Infinity 
 
Dzielenie przez zero działa tylko dla float i double. 
Dla typów całkowitych powoduje ArithmeticException. 
 
System.out.println("abs(-0) = " + Math.abs(-0));    // abs(-0) = 0 
 
 
Typy prymitywne od najwęższego do najszerszego: byte, short, int, long, float, double. 
Typ węższy może być rzutowany niejawnie na typ większy. 
 
Podobnie jak w przypadku klasy String, obiekty klas opakowujących (wrappers)
typy prymitywne są niezmienne. 
 
Klasy opakowujące są finalne - nie można po nich dziedziczyć. 
 
 
Typ prymitywny       | Wrapper Class           | Argumenty konstruktora 
---------------------+-------------------------+----------------------- 
boolean              | Boolean                 | boolean, String 
byte                 | Byte                    | byte, String 
char                 | Character               | char 
double               | Double                  | double, String 
float                | Float                   | float, String 
int                  | Integer                 | int, String 
long                 | Long                    | long, String 
short                | Short                   | short, String 
---------------------+-------------------------+------------------------ 
 
Integer i1 = new Integer(42); 
Integer i2 = new Integer("42"); 
Float f1 = new Float(3.14f); 
Float f2 = new Float("3.14f"); 
 
Character c1 = new Character('c');    // Character ma jedynie konstruktor pobierający char 
 
Boolean b = new Boolean("false"); 
if (b)    // nie skompiluje się, gdyż w tym miejscu powinien być typ boolean, a nie Boolean 
 
 
valueOf()    -- metoda statyczna dostarczana przez większość klas opakowujących 
 
            Integer i2 = Integer.valueOf("101011", 2);    // konwertuje 101011 na 43 
            Float f2 = Float.valueOf("3.14f"); 
 
 
XXXalue()   -- każda z sześciu klas opakowujących posiada sześć metod konwersji 
 
            Integer i2 = new Integer(42); 
            byte b = i2.byteValue(); 
            short s = i2.shortValue(); 
            double d = i2.doubleValue(); 
 
            Float f2 = new Float(3.14f); 
            short s = f2.shortValue(); 
            System.out.println(s);    // 3 
 
 
parseXXX()   -- zwraca typ prymitywny 
 
            double d4 = Double.parseDouble("3.14"); 
            System.out.println("d4 = " + d4);    // d4 = 3.14 
 
            Double d5 = Double.valueOf("3.14"); 
            System.out.println(d5 instanceof Double);    // true 
 
            long l2 = Long.parseLong("101010", 2); 
            System.out.println("L2 = " + L2);    // L2 = 42 
 
            Long l3 = Long.valueOf("101010", 2); 
            System.out.println("L3 value = " + L3);    // L3 = 42 
 
 
toString() 
 
	    Każda klasa opakowująca ma bezargumentową, niestatyczną, instancyjną
            wersję metody toString(). 
 
            Double d = new Double("3.14"); 
            System.out.println("d = " + d.toString());    // d = 3.14 
 
            Każda numeryczna klasa opakowująca posiada przeciążoną statyczną metodę toString(), 
	    która bierze jako argument numeryczny typ prymitywny ( Double.toString(double),
            Long.toString(long) ). 
 
            System.out.println("d = " + Double.toString(3.14));    // d = 3.14 
 
	    Integer i Long posiadają trzecią metodę toString(), która jest statyczna
            i ma argumenty: 
	    wartość typu prymitywnego, radix. Metoda pobiera pierwszy argument (zakłada się,
            że jest on w systemie dziesiętnym) i konwertuje go do systemu przy podstawie
            określonej przez radix, a następnie zwraca String. 
 
            System.out.println("hex = " + Long.toString(254, 16);    // hex = fe 
 
 
toBinaryString(), 
toHexString(), 
toOctalString() 
 
            String s3 = Integer.toHexString(254); 
            System.out.println("254 in hex = " + s3);    // 254 in hex = fe 
 
            String s4 = Long.toOctalString(254); 
            System.out.println("254 in octal = " + s4);  // 254 in octal = 376 
 
 
 
 
Metoda               | Boolean | Byte | Character | Double | Float | Integer | Long | Short 
s = static           |         |      |           |        |       |         |      | 
n = NFE exception    |         |      |           |        |       |         |      |  
---------------------+---------+------+-----------+--------+-------+---------+------+------- 
byteValue            |         |   +  |           |    +   |   +   |    +    |   +  |   + 
                     |         |      |           |        |       |         |      | 
doubleValue          |         |   +  |           |    +   |   +   |    +    |   +  |   + 
                     |         |      |           |        |       |         |      | 
floatValue           |         |   +  |           |    +   |   +   |    +    |   +  |   + 
                     |         |      |           |        |       |         |      | 
intValue             |         |   +  |           |    +   |   +   |    +    |   +  |   + 
                     |         |      |           |        |       |         |      | 
longValue            |         |   +  |           |    +   |   +   |    +    |   +  |   + 
                     |         |      |           |        |       |         |      | 
shortValue           |         |   +  |           |    +   |   +   |    +    |   +  |   + 
---------------------+---------+------+-----------+--------+-------+---------+------+------- 
parseXxx     s,n     |         |   +  |           |    +   |   +   |    +    |   +  |   + 
                     |         |      |           |        |       |         |      | 
parseXxx     s,n     |         |   +  |           |        |       |    +    |   +  |   + 
(with radix)         |         |      |           |        |       |         |      | 
---------------------+---------+------+-----------+--------+-------+---------+------+------- 
valueOf      s,n     |    +    |   +  |           |    +   |   +   |    +    |   +  |   + 
                     |         |      |           |        |       |         |      | 
valueOf      s,n     |         |   +  |           |        |       |    +    |   +  |   + 
(with radix)         |         |      |           |        |       |         |      | 
---------------------+---------+------+-----------+--------+-------+---------+------+------- 
toString             |    +    |   +  |     +     |    +   |   +   |    +    |   +  |   + 
                     |         |      |           |        |       |         |      | 
toString      s      |         |   +  |           |    +   |   +   |    +    |   +  |   + 
(primitive)          |         |      |           |        |       |         |      | 
                     |         |      |           |        |       |         |      | 
toString      s      |         |      |           |        |       |    +    |   +  | 
(primitive, radix)   |         |      |           |        |       |         |      | 
---------------------+---------+------+-----------+--------+-------+---------+------+------- 
toBinaryString   s   |         |      |           |        |       |    +    |   +  | 
                     |         |      |           |        |       |         |      | 
toHexString      s   |         |      |           |        |       |    +    |   +  | 
                     |         |      |           |        |       |         |      | 
toOctalString    s   |         |      |           |        |       |    +    |   +  | 
---------------------+---------+------+-----------+--------+-------+---------+------+------- 
NFE = Number Format Exception 
 

 
==, equals() 
 
	    Używając == do porównania dwóch zmiennych referencyjnych, w istocie porównujemy,
            czy te dwie zmienne wskazują na ten sam obiekt.

            W Object equals() zachowuje się tak samo jak ==, czyli porównuje referencje.

	    Porównując zmienne referencyjne za pomocą == można porównywać jedynie
            zmienne referencyjne, które odnoszą się do obiektów, które są tej samej
            klasy bądź w tej samej hierarchii dziedziczenia klas. 
	    Próba porównania obiektów znajdujących się w różnych hierarchiach
            klas zakończy się błędem kompilacji. 
 
            String x1 = "abc"; 
            String x2 = "ab"; 
            x2 = x2 + "c"; 
 
            if (x1 != x2) 
                system.out.println("different objects"); 
 
            if (x1.equals(x2)) 
                System.out.println("same values"); 
 
 
            ************************************************************************************
	    * Metody equals() zawarte w klasach opakowujących zwracają true wyłącznie wtedy, *
	    * gdy zarówno porównywane wartości prymitywne są równe i klasy opakowujące     *
	    * są te same!                                                                       *
            ************************************************************************************
 
            Double d1 = new Double("3.0"); 
            Integer i1 = new Integer(3); 
 
            if (d1.equals(i1)) 
                System.out.println("wraps are equal");       // brak wyjścia ; klasy są różne! 
 
            Double d2 = d1.valueOf("3.0d"); 
            if (d1.equals(d2)) 
                System.out.println("Doubles are equal");     // Doubles are equal 
 
 
            equals(): 
            1. equals() jest używana wyłącznie do porównywania obiektów. 
            2. equals() zwraca typ boolean. 
	    3. StringBuffer nie posiada nadpisanej metody equals() - pozostawia ją w postaci,
               jaka była w Object. 
	       Oznacza to, że klasa StringBuffer nie ma wbudowanej metody,
               która pozwala powównać łańcuchy znaków 
               zawarte w dwóch obiektach typu StringBuffer. 
	    4. String i klasy opakowujące typy prymitywne są final i mają nadpisane
               metody equals(). 
 
               StringBuffer s1 = new StringBuffer("ala"); 
               StringBuffer s2 = new StringBuffer("ala"); 
        
               if (s1.equals(s2)) 
                   System.out.print("ok");    // brak wyjścia 
 
 
               StringBuffer s1 = new StringBuffer("ala"); 
               StringBuffer s2 = s1; 
 
               if (s1.equals(s2)) 
                   System.out.print("ok");    // ok 
 
 
---- 
 
        String s = "42"; 
        try { 
            s = s.concat(".5"); 
            double d = Double.parseDouble(s); 
            s = Double.toString(d); 
            int x = (int)Math.ceil(Double.valueOf(s).doubleValue()); 
            System.out.println(x);  // 43 
        } catch (NumberFormatException e) { 
            System.out.println("bad number"); 
        } 
 
************************** 
 
Nadpisywanie metody toString() 
 
            public class HardToRead { 
                public static void main(String[] args) { 
                    HardToRead h = new HardToRead(); 
		    System.out.println(h);    // HardToRead@a47e0   <- nazwa klasy
                                              // i unsigned hex hashcode obiektu 
                } 
            } 
 
            ---- 
 
            class Romek { 
                public String toString() { 
                    return "Jestem wesoły Romek!"; 
                } 
            } 
 
            public class Main { 
                public static void main(String[] args) { 
                    System.out.println(new Romek());    // Jestem wesoły Romek! 
                } 
            } 
 
---- 
 
Należy przeciążyć metodę equals(), aby móc użyć danego obiektu jako klucza w hashtable. 
 
Jeśli equals() nie zostanie nadpisana, będzie ona używać operatora == do porównań. 
Zatem po wstawieniu obiektu np. do HashMap jako klucza, potrzebowalibyśmy dokładnie
tego samego obiektu (tej samej instancji), aby wydobyć wartość skojarzoną z kluczem. 
Z tego względu, należy nadpisać metodę equals(), aby porównywała obiekty na podstawie
ich istotnych atrybutów (tak, aby można było stwierdzić, że dwie różne instancje obiektu
są takie same), a nie czy referencje wskazują na ten sam obiekt. 
 
 
public class EqualsTest { 
    public static void main(String[] args) { 
        Moof one = new Moof(8); 
        Moof two = new Moof(8); 
        if (one.equals(two)) 
            System.out.println("one and two are equals"); 
    } 
} 
 
class Moof { 
    private int moofValue; 
    Moof(int val) { 
        moofValue = val; 
    } 
    public int getMoofValue() { 
        return moofValue; 
    } 
    public boolean equals(Object o) { 
        if ((o instanceof Moof) && (((Moof)o).getMoofValue() == this.moofValue)) 
            return true; 
        else 
            return false; 
    } 
} 
 
---- 
 
equals(), hashCode(), toString() są public. 
 
class Foo { 
    boolean equals(Object o) { }    // błąd - equals() powinno być public 
} 
 
class Boo { 
    public boolean equals(Boo b) { }    // błąd - mamy do czynienia z przeciążeniem,
                                        //          a nie nadpisaniem metody 
} 
 
---- 
 
Istnieje zestaw zasad (contract) specyfikujący, jak powinna wyglądać
implementacja metody equals(). 
 
The equals() Contract: 
1. Jest zwrotny (reflexive):      x.equals(x) powinno zwracać true. 
 
2. Jest symetryczny:              x.equals(y) powinno zwracać true, wyłącznie wtedy,
                                      gdy y.equals(x) zwraca true. 
 
3. Jest przechodzni (transitive): jeśli x.equals(y) zwraca true i y.equals(z) zwraca true,
                                      to x.equals(z) powinno zwracać true. 
 
4. Jest konsekwentny:             wielokrotne wywołanie x.equals(y) powinno konsekwentnie
                                  za każdym razem zwracać tę samą wartość - przy założeniu,
                                  że w między czasie stan obiektu nie zmienił stanu. 
 
5.                                dla każdej referencji niebędącej null, x.equals(null)
                                  powinno zwracać false. 
 
 
Jeśli dwa obiekty są równe (equals() zwraca true), to muszą one mieć identyczne
wartości hashcode. 
 
Zasada praktyczna: jeśli następuje przesłonięcie metody equals(), należy przesłonić
także metodę hashCode(). 
 
Metoda equals() na ogół porównuje wartości zmiennych instancyjnych, aby określić,
czy obiekty są równe. 
Metoda hashCode() powinna posługiwać się tymi samymi zmiennymi. 
 
 
class Hash { 
    private int x; 
 
    Hash(int x) { 
        this.x = x; 
    } 
 
    public boolean equals(Object o) { 
        Hash h = (Hash)o;    // normalnie powinno następować wcześniej sprawdzenie instanceof 
 
        if (h.x == this.x) 
            return true; 
        else 
            return false; 
    } 
 
    public int hashCode() { 
        return x * 17; 
    } 
} 
 
---- 
 
public int hashCode() { 
    return 1492; 
} 
 
Powyższa implementacja zwraca taką samą wartość dla wszystkich instancji, niezależnie od tego, 
czy są one równe, czy nie. Mimo to, jest to wciąż legalne (legal) i nawet
właściwa (appropriate) metoda hashCode(), gdyż nie łamie zasad (contract) implementacji
określonych w specyfikacji. 
Dwa obiekty z "x" równym 8 będą miały takie same hashcode'y. 
Ta metoda jest ekstremalnie nieefektywna - wszystkie obiekty wylądują w tym samym miejscu. 
Jakkolwiek jest ona poprawna i nie łamie zasad. 
 
Contract wymaga jedynie, aby dwa równe obiekty (które mają np. wartości pewnych zmiennych równe) 
miały takie same hashcode'y. 
 
The hashCode() Contract: 
 
1. Wielokrotne uruchomienie hashCode() dla tego samego obiektu musi zwracać tę samą
   wartość integer.
   Wartość musi być stała podczas działania programu, ale może być inna pomiędzy
   różnymi uruchomieniami.
 
2. Jeśli dwa obiekty są równe (equals() zwraca true), wtedy hashCode() dla każdego
   z tych obiektów musi mieć tę samą wartość. 
 
3. Nie jest wymagane, że jeśli dwa obiekty nie są równe, muszą mieć inne wartości
   zwracane przez hashCode(). 
   Jakkolwiek dla większej efektywności, różne obiekty powinny mieć inne hashcode'y. 
 
 
Stan                            | Wymagane                     | Nie wymagane (ale dozwolone) 
--------------------------------+------------------------------+-------------------------------- 
x.equals(y) == true             | x.hashCode() == y.hashCode() | 
                                |                              | 
x.hashCode() == y.hashCode()    |                              | x.equals(y) == true 
                                |                              | 
x.equals(y) == false            |                              | brak wymagań dla hashCode() 
                                |                              | 
x.hashCode() != y.hashCode()    | x.equals(y) == false         | 
--------------------------------+------------------------------+-------------------------------- 
 
 
W hashCode() nie powinno się używać zmiennych "transient", gdyż nie są one zapisywane
podczas serializacji i może być problem z odnalezieniem potem obiektu w kolekcji. 
 
---- 
 
Kolekcje 
 
The core interfaces: Collection, Set, Sorted Set, List, Map, Sorted Map 
The core concrete implementation classes:  
                   Map implementations: HashMap, Hashtable, TreeMap, LinkedHashMap   
                                                           (uwaga: Hashtable, a nie HashTable) 
                   Set implementations: HashSet, LinkedHashSet, TreeSet 
                   List implementations: ArrayList, Vector, LinkedList 
 
Nie wszystkie kolekcje (collections) w Collections Framework implementują interfejs Collection. 
Żadna z klas i interfejsów Map* nie implementuje Collection - choć SortedMap, Hashtable,
HashMap, TreeMap, LinkedHashMap należą do kolekcji (collections), nie dziedziczą po Collection. 
 
 
java.util.Collection - interfejs, który rozszerza Set i List. 
 
java.util.Collections - klasa, która posiada statyczne metody operujące na kolekcjach
                        (np. add, remove, contains, size, iterator).
 
 
        dz. - dziedziczenie 
        imp. - implementacja 
                                                 Collection 
                                    dz.            /   \             dz. 
                                -------------------     -------------------              
                               /                                           \           
                             Set                                          List 
                   imp.     / | \     dz.                                / |  \ 
                 -----------  |  ----------                             /  |   \ 
                /             |            \                           /   |    \ 
               /              |         SortedSet                   ---    |     --- 
              /           imp.|             |                  imp./   imp.|        \imp. 
             /                |             |imp.                 /        |         \ 
            /                 |             |                    /         |          \ 
          HashSet      LinkedHashSet     TreeSet           LinkedList   Vector    ArrayList 
 
 
 
 
                                                   Map 
                                                  /| |\ 
                                                 / | | \     dz. 
                                      -----------  | |  ----------- 
                                     /            /   \            \ 
                                    /            /     \            \ 
                               imp./        imp./       \imp.     SortedMap 
                                  /            /         \            \ 
                                 /            /           \            \imp. 
                                /            /             \            \ 
                            Hashtable   LinkedHashMap    HashMap      TreeMap 
 
 
List - lista obiektów 
Set - lista unikalnych obiektów 
Map - Obiekty z unikalnym ID 
 
Każdy z tych trzech typów może być: Sorted, Unsorted, Ordered, Unordered. 
 
Klasy implementujące mogą być: unsorted & unordered, ordered but unsorted, ordered & sorted. 
Klasy implementujące nigdy nie są: sorted but unordered. 
 
 
Ordered 
------- 
 
Kolekcja jest ordered, jeśli możliwa jest iteracja przez kolekcję w pewnej (nielosowej)
kolejności.
Hashtable nie jest ordered, gdyż kolejność elementów przy iteracji jest nieprzewidywalna. 
ArrayList jest ordered, gdyż kolejność oparta jest na indeksie elementów. 
LinkedHashSet jest ordered, gdyż posiada kolejność zależną od kolejności wstawiania elementów. 
Są kolekcje, w których kolejność (order) odnosi się do tzw. natural order - takie kolekcje są 
zarówno ordered i sorted. 
 
Natural order: - dla kolekcji obiektów String, natural order to porządek alfabetyczny, 
	       - dla kolekcji obiektów Integer, natural order to porządek według wartości
                 numerycznych, 
	       - dla kolekcji obiektów Foo, natural order musi zostać zdefiniowany
                 przez programistę. 
 
Sorted 
------ 
 
Kolekcja sorted oznacza kolekcję "sorted by natural order". 
Natural order is defined by the class of the objects being sorted. 
 
---- 
 
// A LinkedHashSet, ordered by order of insertion 
 
import java.util.*; 
 
public class Ordered { 
    public static void main(String[] args) { 
        LinkedHashSet lhs = new LinkedHashSet(); 
        lhs.add("Chicago"); 
        lhs.add("Detroit"); 
        lhs.add("Atlanta"); 
        lhs.add("Denver"); 
        lhs.add("Denver"); 
         
        Iterator it = lhs.iterator();         
        while (it.hasNext()) 
            System.out.println("city " + it.next()); 
        // city Chicago 
        // city Detroit 
        // city Atlanta 
        // city Denver 
    } 
} 
 
---- 
 
// A TreeSet, sorted alphabetically 
 
import java.util.*; 
 
public class Sorted { 
    public static void main(String[] args) { 
        TreeSet ts = new TreeSet(); 
        ts.add("Chicago"); 
        ts.add("Detroit"); 
        ts.add("Atlanta"); 
        ts.add("Denver"); 
        ts.add("Denver"); 
         
        Iterator it = ts.iterator(); 
        while (it.hasNext()) 
            System.out.println("city " + it.next()); 
         
        // city Atlanta 
        // city Chicago 
        // city Denver 
        // city Detroit 
    } 
} 
 
---- 
 
List 
---- 
    Lista umożliwia operowanie na indeksach. Posiada m.in. metody: get(int index), indexOf(), 
    add(int index, Object obj). 
    Wszystkie trzy implementacje List są ordered pod względem indeksu. 
 
ArrayList 
--------- 
    O ArrayList można myśleć jako o rosnącej tablicy. 
    ArrayList umożliwia szybką iterację i szybki swobodny dostęp. 
    Jest to kolekcja ordered (poprzez indeks), ale nie jest sorted. 
    Od Javy 1.4 ArrayList implementuje interfejs RandomAccess - tzw. marked
    interface (bez metod), aby zaznaczyć, że lista posiada szybki
    (w czasie stałym) dostęp. 
    ArrayList należy wybrać, a odrzucić LinkedList, jeśli potrzbujemy
    szybkiego iteratora, ale szybkość operacji wstawiania i usuwania
    elementów jest mniej istotna. 
 
Vector 
------ 
    Vector jest podobny do ArrayList. Vector posiada synchronizowane
    metody (bezpieczne dla używania z wątkami), które wpływają
    negatywnie na wydajność. 
    Klasy Vector i ArrayList są jedynymi, które umożliwiają swobodny (RandomAccess) dostęp
    do elementów. 
 
LinkedList 
---------- 
    LinkedList jest ordered pod względem indeksu. Elementy są podwójnie linkowane do innych. 
    To linkowanie oraz nowe metody dodawania i usuwania elementów z początku i końca
    sprawiają, że 
    LinkedList jest dobrym wyborem do implementacji stosu bądź kolejki. 
    LinkedList ma wolniejszy iterator niż ArrayList, ale szybsze wstawianie i usuwanie elementów. 
 
Set 
--- 
    Set nie pozwala na duplikowanie elementów. Porównywanie elementów odbywa się
    za pomocą metody equals(). 
 
HashSet 
------- 
    HashSet jest unsorted i unordered. Używa wartości zwracanej przez hashCode(). 
    Nie pozwala na duplikaty elementów. 
 
LinkedHashSet 
------------- 
    Jest to wersja ordered klasy HashSet, która posiada podwójne linkowanie do pozostałych
    elementów. 
    LinkedHashSet pozwala na iterację w kolejności wstawiania elementów, bądź
    opcjonalnie w kolejności ostatnich dostępów do elementów. LinkedHashSet jest
    nowością w Javie 1.4. 
    Kolekcja ta używana jest dla celów cache'ujących - FIFO. 
 
TreeSet 
------- 
    TreeSet i TreeMap są jedynymi dwiema kolekcjami sorted. 
    Używa drzew czerwono-czarnych i gwarantuje, że elementy będą w idącym w górę
    porządku (order), zgodnym z natural order elementów. 
    Można także wprowadzić własne zasady dla natural order. 
 
Map 
--- 
    Za pomocą Map mapuje się unikatowy klucz (key, ID) na wartość (value).
    Klucz i wartość są obiektami. 
    Klucze opierają się na hashcode'ach. 
    Można: wyszukiwać wartości bazując na kluczu, zapytać o kolekcję o danej wartości,
    zapytać o kolekcję o danym kluczu. 
 
HashMap 
------- 
    HashMap to unsorted i unordered Map. 
    HashMap pozwala mieć jeden klucz null i wiele wartości null w kolekcji. 
 
Hashtable 
--------- 
    Hashtable jest synchronicznym odpowiednikiem HashMap. 
    Hashtable nie pozwala na posiadanie czegokolwiek o wartości null. 
 
LinkedHashMap 
------------- 
    Podobnie jak LinkedHashSet, LinkedHashMap posiada porządek (order) według kolejności
    wstawiania elementów 
    (opcjonalnie, access order). 
    LinkedHashMap posiada wolniejsze wstawianie i usuwanie elementów, niż HashMap. 
    LinkedHashMap posiada szybszy iterator niż HashMap. 
 
TreeMap 
------- 
    TreeMap jest sorted. Można wprowadzić własną metodę porównując, aby określić
    reguły porządku naturalnego. 
 
---- 
 
Map nie może być klasą kolekcji, gdyż Map jest interfejsem, a nie konkretną
implementacją klasy. 
Należy zwrócić uwagę na podstępne pytania w tym zakresie. 
 
 
 
Klasa          Map     Set     List       Ordered            Sorted 
--------------------------------------------------------------------------------------------- 
HashMap         +                         Nie                Nie 
 
Hashtable       +                         Nie                Nie 
 
TreeMap         +                         Sorted             Natural order bądź własne reguły 
 
LinkedHashMap   +                         insertion order    Nie 
                                          last access order 
 
HashSet                 +                 Nie                Nie 
 
TreeSet                 +                 Sorted             Natural order bądź własne reguły 
 
LinkedHashSet           +                 insertion order    Nie 
                                          last access order 
 
ArrayList                        +        Przez indeks       Nie 
 
Vector                           +        Przez indeks       Nie 
 
LinkedList                       +        Przez indeks       Nie 
---------------------------------------------------------------------------------------------- 
 
 
ArrayList:     szybka iteracja i szybki swobodny dostęp; wolniejsze niż w liście wstawianie
               i usuwanie elementów. 
 
Vector:        Wolniejszy niż ArrayList, głównie z powodu metod synchronized. 
 
LinkedList:    Przydatne, gdy trzeba dodawać elementy na koniec, np. w stosach i kolejkach. 
 
HashSet:       Brak duplikatów; brak porządku (order). 
 
LinkedHashSet: Brak duplikatów;  iteracja według kolejności wstawiania bądź ostatniego
               dostępu (nowość w Java 1.4). 
 
TreeSet:       Brak duplikatów; iteracja w natural sorted order. 
 
HashMap:       Najszybszy update (klucz/wartość); zezwala na jeden klucz null i wiele
               wartości null. 
 
Hashtable:     Jak wolniejszy HashMap z powodu metod synchronicznych. Nie zezwala na klucze
               i wartości null. 
 
LinkedHashMap: Szybszy iterator; iteracja według porządku wstawiania bądź ostatniego dostępu;  
               zezwala na jeden klucz null i wiele wartości null (nowość w Java 1.4). 
 
TreeMap:       Sorted map; natural order. 
 
 
 
Garbage Collector 
----------------- 
 
Garbage Collector zajmuje się obiektami tworzonymi na stercie (heap, garbage collectible heap). 
 
Zadaniem Garbage Collectora jest odnajdowanie i usuwanie obiektów, które nie są
osiągalne (cannot be reached), czyli takich, do których nie istnieją referencje. 
 
Object eligile for garbage collection - obiekt, który może zostać usunięty przez
garbage collector. 
 
Obiekt może zostać usunięty przez garbage collector, kiedy żaden żyjący wątek nie ma
do niego dostępu. 
 
Aplikacja napisana w Javie może mieć stworzonych na tyle dużo żyjących obiektów,
że wyczerpią się zasoby pamięci. 
 
 
StringBuffer sb = new StringBuffer("hello"); 
System.out.println(sb); 
// The StringBuffer object is not eligible for collection 
sb = null; 
// Now the StringBuffer object is eligible for collection 
 
To że obiekt nie jest używany, nie oznacza, że jest eligible for collection. 
Dopiero brak referencji do obiektu oznacza, że jest on eligible for collection. 
 
---- 
 
StringBuffer s1 = new StringBuffer("hello"); 
StringBuffer s2 = new StringBuffer("goodbye"); 
System.out.println(s1); 
// At this point the StringBuffer "hello" is not eligible 
s1 = s2; 
// Now the StringBuffer "hello" is eligible for collection 
 
---- 
 
Isolating a Reference (island of objects) 
 
 
public class Island { 
    Island i; 
 
    public static void main(String[] args) { 
        Island i2 = new Island(); 
        Island i3 = new Island(); 
        Island i4 = new Island(); 
 
        i2.i = i3; 
        i3.i = i4; 
        i4.i = i2; 
 
        i2 = null; 
        i3 = null; 
        i4 = null; 
    } 
} 
 
Obiekty mają zmienne instancyjne wskazujące na siebie, ale nie mają połączenia
ze światem zewnętrznym. Te trzy obiekty są elgible for garbage collection. 
 
---- 
 
import java.util.Date; 
public class GarbageFactory { 
    public static void main(String[] args) { 
        Date d = getDate(); 
        doComplicatedStuff(); 
        System.out.println("d = " + d); 
    } 
 
    public static Date getDate() { 
        Date d2 = new Date(); 
        String now = d2.toString();   // "now" kończy swoje istnienie, wraz z końcem funkcji 
        System.out.println(now); 
        return d2;                    // "d2" nie jest tracony, mimo zakończenia funkcji 
    } 
} 
 
---- 
 
Zadziałanie garbage collectora nie może być wymuszone, choć istnieją pewne metody, które 
mogą zgłosić do JVM potrzebę wykonania garbage collectora. Nie ma jednak gwarancji, że JVM 
zostanie uruchomione i usunie wszystkie nieużywane obiekty. 
 
Gwarantowane jest, że gdy jest mało pamięci, garbage collector zostanie uruchomiony 
zanim zostanie wyrzucony OutOfMemoryException. 
 
Klasa Runtime jest specjalną klasą, która posiada pojedynczy obiekt (Singleton) dla każdego 
głównego programu. Obiekt Runtime posiada mechanizm umożliwiający komunikację
z maszyną wirtualną.
Aby dostać instancję Runtime, można użyć metody Runtime.getRuntime(), która zwraca Singleton. 
Można także skorzystać z klasy System, która ma metody statyczne, pośrednio
pobierającego Singleton. 
Najprostsza metoda poproszenia o wykonanie garbage collection jest: 
 
    System.gc(); 
 
Jednak specyfikacja języka pozwala, aby powyższa metoda nie robiła nic. 
 
---- 
 
import java.util.Date; 
public class CheckGC { 
    public static void main(String[] args) { 
        Runtime rt = Runtime.getRuntime(); 
        System.out.println("Total JVM memory: " + rt.totalMemory()); 
        System.out.println("Beore Memory = " + rt.freeMemory()); 
        Date d = null; 
        for (int i = 0; i < 10000; i++) { 
            d = new Date(); 
            d = null; 
        } 
        System.out.println("Afted Memory = " + rt.freeMemory()); 
        rt.gc();    // an alternate to System.gc() 
        System.out.println("After GC Memory = " + rt.freeMemory()); 
    } 
} 
 
Total JVM memory: 1048568 
Before Memory = 703008 
After Memory = 458048 
After GC Memory = 818272 
 
Zachowanie gc() znależy od implementacji JVM. 
 
---- 
 
Kod umieszczony w metodzie finalize() zostanie uruchomiony tuż przed usunięciem obiektu przez 
garbage collector. 
Nie jest jednak zagwarantowane, że kod umieszczony w finalize() zostanie kiedykolwiek wykonany. 
 
Dla każdego obiektu, finalize() zostanie wywołane tylko raz przez garbage collector. 
Wywołanie finalize() może uchronić obiekt przed usunięciem. 
Załużmy, że garbage collector wywołał finalize() dla pewnego obiektu. W metodzie finalize() 
zainicjowano jakąś zewnętrzną referencję na usuwany obiekt - obiekt staje się
not eigible for garbage collection i nie zostanie usunięty. Jednak garbage collector
pamięta, że finalize() było już raz wykonane dla tego obiektu i nie zostanie ponownie
wywołane, gdy obiekt ponownie stanie się eligible for garbage collection. 
 
 
Jak sprawić, żeby zaalokowany obiekt nie był nigdy usunięty? 
Można stworzyć statyczną zmienną w klasie, będącą referencją do tego obiektu. 
 
---- 
 
equals(), hashCode() i toString() są public. 
finalize() jest protected. 
 
 
Inner Classes 
************* 
 
Instancja klasy wewnętrznej ma dostęp do wszystkich elementów klasy zewnętrznej,
nawet tych prywatnych. 
 
class MyOuter { 
    class MyInner { } 
} 
 
% javac MyOuter.java 
 
MyOuter.class 
MyOuter$MyInner.class 
 
---- 
 
Klasy wewnętrzne nie mogą mieć żadnych deklaracji static. 
Dostęp do klasy wewnętrznej jest możliwy jedynie przez żywą instancję klasy zewnętrznej. 
Aby utworzyć instancję klasy wewnętrznej, musi istnieć instancja klasy zewnętrznej
(nie można np. stworzyć obiektu klasy wewnętrznej z poziomu statycznej metody klasy zewnętrznej). 
 
---- 
 
class MyOuter { 
    private int x = 7; 
 
    class MyInner { 
        public void seeOuter() { 
            System.out.println("Outer x is " + X); 
        } 
    } 
} 
 
---- 
 
class MyOuter { 
    private int x = 7; 
    public void makeInner() { 
        MyInner in = new MyInner(); 
        in.seeOuter(); 
    } 
 
    class MyInner { 
        public void seeOuter() { 
            System.out.println("Outer x is " + x); 
        } 
    } 
} 
 
---- 
 
public static void main(String[] args) { 
    MyOuter mo = new MyOuter(); 
    MyOuter.MyInner inner = mo.new MyInner(); 
    inner.seeOuter(); 
} 
 
---- 
 
public static void main(String[] args) { 
    MyOuter.MyInner inner = new MyOuter().new MyInner(); 
    inner.seeOuter(); 
} 
 
Powyższe dwa przykłady pozwalają na stworzenie obiektu klasy wewnętrznej z zewnątrz klasy 
zewnętrznej bądź z metody statycznej znajdującej się w klasie zewnętrznej. 
 
---- 
 
this może być użyte tylko w kodzie instancyjnym (nie static). 
 
public void myMethod() { 
    MyClass mc = new MyClass(); 
    mc.doStuff(this);    // pass a ref to object running MyMethod 
} 
 
---- 
 
class MyOuter { 
    private int x = 7; 
    public void makeInner() { 
        MyInner in = new MyInner(); 
        in.seeOuter(); 
    } 
 
    class MyInner { 
        public void seeOuter() { 
            System.out.println("Outer x is " + x); 
            System.out.println("Inner class ref is " + this); 
            System.out.println("Outer class ref is " + MyOuter.this); 
        } 
    } 
 
    public static void main(String[] args) { 
        MyOuter.MyInner inner = new MyOuter().new Inner(); 
        inner.seeOuter(); 
    } 
} 
 
 
Outer x is 7 
Inner class ref is MyOuter$MyInner@113708 
Outer class ref is MyOuter@33f1d7 
 
---- 
 
Klasy wewnętrzne mogą być zadeklarowane z modyfikatorami: 
final 
abstract 
public 
private 
protected 
static - mamy wtedy top-level nested class, a nie inner class 
 
---- 
 
Method-Local Inner Classes 
 
class MyOuter2 { 
    private String x = "Outer2"; 
 
    void doStuff() { 
        class MyInner { 
            public void seeOuter() { 
                System.out.println("Outer x is " + x); 
            } 
        } 
    } 
} 
 
---- 
 
class MyOuter2 { 
    private String x = "Outer2"; 
    void doStuff() { 
        class MyInner { 
            public void seeOuter() { 
                System.out.println("Outer x is " + x); 
            } 
        } 
 
        MyInner mi = new MyInner(); // Ten wiersz musi być po definicji klasy 
 
        mi.seeOuter(); 
    } 
} 
 
Obiekt method-local inner class może być utworzony wyłącznie wewnątrz metody, w której 
klasa wewnętrzna jest zdefiniowana. 
 
Method-local inner class ma dostęp do elementów klasy otaczającej. 
 
---- 
 
Obiekt klasy wewnętrznej nie może używać zmiennych lokalnych metody (poza zmiennymi final), 
w której klasa ta została zdefiniowana. 
 
class MyOuter2 { 
    private String x = "Outer2"; 
    void doStuff() { 
        String z = "local variable"; 
 
        final String z2 = "local variable"; 
 
        class MyInner { 
            public void seeOuter() { 
                System.out.println("Outer x is " + x); 
                System.out.println("Local variable z is " + z);   // Błąd kompilacji 
                System.out.println("Local variable z2 is " + z2); // OK 
            } 
        } 
    } 
} 
 
 
Podobnie jak w przypadku zmiennych lokalnych, klasy wewnętrzne zadeklarowane w metodach nie 
mogą być: public, private, protected, static, transient, itp. 
 
Można zastosować abstract bądź final (ale nie obie naraz). 
 
---- 
 
Anonymous Inner Classes 
 
class Popcorn { 
    public void pop() { 
        System.out.println("popcorn"); 
    } 
} 
 
class Food { 
    Popcorn p = new Popcorn() { 
        public void pop() { 
            System.out.println("anonymous popcorn"); 
        } 
    };                // ; 
} 
 
---- 
 
class Popcorn { 
    public void pop() { 
        System.out.println("popcorn"); 
    } 
} 
 
class Food { 
    Popcorn p = new Popcorn() { 
        public void sizzle() { 
            System.out.println("anonymous sizzling popcorn"); 
        } 
        public void pop() { 
            System.out.println("anonymous popcorn"); 
        } 
    }; 
 
    public void popIt() { 
        p.pop();    // OK, Popcorn ma metodę pop() 
        p.sizzle(); // Błąd kompilacji! Popcorn nie ma metody sizzle() 
    } 
} 
 
---- 
 
interface Cookable { 
    public void cook(); 
} 
 
class Food { 
    Cookable c = new Cookable() { 
        public void cook() { 
            System.out.println("anonymous cookable implementer"); 
        } 
    }; 
} 
 
Anonimowy implementator interfejsu może implementować tylko jeden interfejs. 
Wewnętrzna klasa anonimowa nie może także jednocześnie rozszerzać klas
i implementować interfejsu. 
 
---- 
 
Runnable r = new Runnable();             // błąd! Runnable to interfejs 
 
Runnable r = new Runnable() { 
                 public void run() { } 
             };                          // OK 
 
---- 
 
Argument-Defined Anonymous Inner Class 
 
class MyWonderfulClass { 
    void go() { 
        Bar b = new Bar(); 
        b.doStuff(new Foo() { 
                      public void foof() { 
                          System.out.println("foofy"); 
                      } 
                  });          // brak średnika po } 
    } 
} 
 
interface Foo { 
    void foof(); 
} 
 
class Bar { 
    void doStuff(Foo f) { } 
} 
 
---- 
 
Static Nested Classes 
 
Static nested classes, top-level nested classes, static inner classes. 
Klasy wewnętrzne statyczne nie są w istocie klasami wewnętrznymi, gdyż ich związek z klasami 
zewnętrznymi jest inny niż w przypadku innych klas wewnętrznych. 
 
class BigOuter { 
    static class Nested { } 
} 
 
static nie oznacza, że klasa jest statyczna (nie ma czegoś takiego jak statyczna klasa), 
ale oznacza, że jest statycznym elementem klasy zewnętrznej. 
Do klasy tej można mieć dostęp, jak do innych statycznych elementów, z wnętrza statycznej 
metody klasy zewnętrznej, bez potrzeby posiadania instancji klasy zewnętrznej. 
 
Podobnie jak metody statyczne, statyczne zagnieżdżone klasy nie mają dostępu do instancyjnych 
zmiennych i metod klasy zewnętrznej. 
 
---- 
 
class BigOuter { 
    static class Nested { } 
} 
 
class Broom { 
    public static void main(String[] args) { 
        BigOuter.Nested n = new BigOuter.Nested(); 
    } 
} 
 
---- 
 
public abstract class AbstractTest { 
    public int getNum() { 
        return 45; 
    } 
    public abstract class Bar { 
        public int getNum() { 
            return 38; 
        } 
    } 
    public static void main(String[] args) { 
        AbstractTest t = new AbstractTest() { 
            public int getNum() { 
                return 22; 
            } 
        }; 
 
        AbstractTest.Bar f = t.new Bar() { 
            public int getNum() { 
                return 57; 
            } 
        }; 
 
        System.out.println(f.getNum() + " " + t.getNum());    // 57 22 
    } 
} 
 
---- 
 
 
 
Threads 
******* 
 
Uruchomiony wątek jest indywidualnym lekkim procesem (lightweight process), który posiada 
własny stos. 
Metoda main() działa w swoim własnym wątku, nazwanym main. 
Każdy wątek ma własny stos wywołań. 
 
Wątek może być stworzony jako: 
- subklasa klasy java.lang.Thread, 
- implementacja interfejsu Runnable 
 
W codziennej praktyce programistycznej, bardziej prawdopodobne będzie użycie Runnable. 
 
Dziedziczenie po klasie Thread, aby stworzyć wątek kłóci się nieco z paradygmatem OO. 
Dziedziczenie po Thread powinno się zastosować raczej w przypadku tworzenia bardziej
specjalizowanej klasy wątków, a nie po to, aby stworzyć wątek. 
Implementacja interfejsu Runnable daje także możliwość, aby klasa implementująca
mogła dziedziczyć po jakiejś klasie. 
 
 
Dziedziczenie po java.lang.Thread 
--------------------------------- 
 
class MyThread extends Thread { 
    public void run() { 
        System.out.println("..."); 
    } 
} 
 
Minusem takiego rozwiązania jest to, że gdy klasa MyThread dziedziczy po Thread, 
nie może dziedziczyć po żadnej dodatkowej klasie. 
 
Można także przeciążyć metodę run(), ale żadna z metod przeciążonych nie zostanie
oczywiście wywołana automatycznie i nie zostanie użyta jako początek nowego stosu
wywołań dla wątku. 
 
Implementacja java.lang.Runnable 
-------------------------------- 
 
class MyRunnable implements Runnable { 
    public void run() { 
        System.out.println("..."); 
    } 
} 
 
---- 
 
Każdy wątek wymaga instancji klasy Thread (niezależnie od tego, czy dziedziczyliśmy po 
Thread, czy implementowaliśmy Runnable). 
 
Thread 
------ 
 
MyThread t = new MyThread(); 
 
 
Runnable 
-------- 
 
MyRunnable r = new MyRunnable(); 
Thread t = new Thread(r); 
 
Stworzenie wątku z użyciem konstruktora bezargumentowego powoduje, że wątek wywoła swoją 
własną metodę run() - jest to zachowanie porządane w przypadku dziedziczenia po Thread. 
W przypadku Runnable, należy przekazać nowemu wątkowi, aby użył naszej metody run(). 
Runnable przekazane do konstruktora klasy Thread jest nazywane "target" bądź "target Runnable". 
Można przekazać pojedynczą instancję Runnable do wielu obiektów klasy Thread, tak że ten sam 
obiekt Runnable stanie się celem (target) dla wielu wątków. 
 
public class TestThreads { 
    public static void main(String[] args) { 
        MyRunnable r = new MyRunnable(); 
        Thread foo = new Thread(r); 
        Thread bar = new Thread(r); 
        Thread bat = new Thread(r); 
    } 
} 
 
---- 
 
Konstruktowy klasy Thread: 
 
- Thread(), 
- Thread(Runnable target), 
- Thread(Runnable target, String name), 
- Thread(String name), 
- Thread(ThreadGroup group, Runnable target), 
- Thread(ThreadGroup group, Runnable target, String name), 
- Thread(ThreadGroup group, String name) 
 
---- 
 
Jeśli wątek jest stworzony, ale nie wystartowany (nie wywołano metody start()), 
mówi się, że wątek jest w nowym stanie (new state). W tym stanie wątek nie jest 
jeszcze żywy (alive, isAlive() zwróci false). 
Wątek jest żywy (alive) po tym jak został wystartowany (po wywołaniu start(), JVM 
potrzebuje nieco czasu na uruchomienie wątka). Wątek nie jest żywy (not alive), po 
tym jak stanie się martwy. 
 
 
Uruchamianie wątku 
------------------ 
 
t.start(); 
 
Po wywołaniu start(), wątek przechodzi do nowego stanu (new state). 
Nowy stan oznacza, że mamy obiekt klasy Thread, ale nie mamy jeszcze prawdziwego wątku
(true thread). 
 
Po wywołaniu start(): 
- startuje nowy wątek (z nowym stosem wywołań), 
- wątek przechodzi z nowego stanu (new state) do runnable state, 
- kiedy wątek otrzymuje możliwość wykonania, jego metoda run() (target run() zostanie wykonana. 
 
---- 
 
Runnable r = new Runnable(); 
r.run();    // legalne, ale nie startuje wątku 
 
---- 
 
public class NameThread { 
    public static void main(String[] args) { 
        System.out.println("thread is " + Thread.currentThread().getName());    // thread is main 
    } 
} 
 
---- 
 
class NameRunnable implements Runnable { 
    public void run() { 
        for (int x = 1; x < 4; x++) 
            System.out.println("Run by " + Thread.currentThread().getName()); 
    } 
} 
 
public class ManyName { 
    public static void main(String[] args) { 
        NameRunnable nr = new NameRunnable(); 
        Thread one = new Thread(nr); 
        one.setName("Fred"); 
        Thread two = new Thread(nr); 
        two.setName("Lucy"); 
        Thread three = new Thread(nr); 
        three.setName("Ricky"); 
        one.start(); 
        two.start(); 
        three.start(); 
    } 
} 
 
---- 
 
Jeśli wykonywanie metody run() zakończyło się, nie można ponownie uruchomić wątku poprzez 
t.start() - próba wywołania start() spowoduje wyrzucenie wyjątku. 
 
---- 
 
Stany wątku 
----------- 
 
New - stan, w którym wątek znajduje się, gdy została utworzona instancja klasy Thread,
      ale nie wywołano start(); 
      W tym stanie wątek określa się jako nieżywy (not alive); 
 
Runnable - stan, w którym wątek znajduje się, gdy może zostać wykonany,
           ale scheduler aktualnie nie wykonuje jego kodu; 
           wątek wchodzi w stan runnable, gdy zostanie wywołana metoda start(); 
           wątek może także wrócić do stanu runnable, ze stanów blocked, waiting, sleeping; 
           w tym stanie wątek określa się jako żywy (alive); 
 
Running - stan, w którym wątek znajduje się, gdy scheduller wybrał go z runnable pool,
          jako aktualnie wykonywany; 
 
Waiting/blocked/sleeping - wątki znajdujące się w tych stanach są ciągle żywe (alive),
                           ale nie mogą zostać wykonane (nie są runnable); 
 
			   wątek może być blocked, oczekując na zasoby - jeśli zasób
                           stanie się dostępny, 
                           wątek stanie się runnable; 
                           wątek zablokowany ciągle jest żywy (alive); 
 
			   wątek może być sleeping, gdy zostanie uśpiony jawnie
                           z poziomu programu - jeśli czas 
                           uśpienia minie, wątek stanie się runnable; 
 
			   wątek może być waiting, gdy zostanie ustawiony w taki stan
                           - jeśli inny wątek 
                           prześle odpowiednią notyfikację, wątek znów stanie się runnable; 
 
Dead - stan, w którym wątek znajduje się, gdy metoda run() zakończy działanie; 
       nieżywy wątek nie może być przywrócony do życia; 
 
 
 
 
                       +--  Waiting/  <--+ 
                       |    blocking     | 
                       |                 | 
                       v                 | 
                        
         New  --->  Runnable  <--->  Running  --->  Dead 
 
 
---- 
 
sleep() 
------- 
 
 
class NameRunnable implements Runnable { 
    public void run() { 
        for (int x = 1; x < 4; x++) { 
            System.out.println("Run by " + Thread.currentThread().getName()); 
            try { 
                Thread.sleep(1000); 
            } catch (InterruptedException ex) { } 
        } 
    } 
} 
 
public class ManyNames { 
    public static void main(String[] args) { 
        nameRunnable nr = new NameRunnable(); 
        Thread one = new Thread(nr); 
        one.setName("Fred"); 
        Thread two = new Thread(nr); 
        two.setName("Lucy"); 
        Thread three = new Thread(nr); 
        three.setName("Ricky"); 
        one.start(); 
        two.start(); 
        three.start(); 
    } 
} 
 
---- 
 
Po upływie czasu sleep() wątek budzi się i przechodzi do stanu runnable. 
Zatem czas ustawiony dla sleep() jest minimalnym czasem, przez których wątek 
nie będzie działał, a nie dokładnym czasem. Nie można polegać na sleep(), 
aby uzyskać dokładny zegar. 
 
sleep() jest metodą statyczną. Jeden wątek nie może uśpić innego wątku. 
 
---- 
 
Wątek zawsze działa z jakimś priorytetem. 
Priorytety numerowane są od 1 do 10 (choć w niektórych przypadkach zakres jest mniejszy niż 10). 
Jeśli np. stworzyliśmy wątki z 10 różnymi priorytetami, a program jest uruchomiony na JVM, 
w której max. priorytet wynosi 5, to niektóre wątki mogą być zmapowane na ten sam priorytet. 
 
Poszczególne implementacje JVM mogą inaczej obsługiwać priorytety wątków, a zachowanie 
schedulera nie jest zagwarantowane. 
 
Nowy wątek otrzymuje domyślny priorytet, który jest priorytetem wątku, który go stworzył. 
 
Domyślny priorytet wynosi 5. 
 
 
public class TestThreads { 
    public static void main(String[] args) { 
        MyThread t = new MyThread(); 
    } 
} 
 
Wątek, do którego odnosi się "t" będzie miał taki sam priorytet, jak wątek "main". 
 
 
Zmiana priorytetu wątku: 
 
FooRunnable r = new FooRunnable(); 
Thread t = new Thread(r); 
t.setPriority(8); 
t.start(); 
 
 
Klasa Thread posiada trzy zmienne static final: 
Thread.MIN_PRIORITY  (1) 
Thread.NORM_PRIORITY (5) 
Thread.MAX_PRIORITY  (10) 
 
 
yield() 
------- 
 
Powoduje przejście aktualnie wykonywanego wątku do puli runnable, 
aby pozwolić innemu wątkowi o tym samym priorytecie, aby został wykonany. 
W rzeczywistości nie ma gwarancji, że yield() wykona to, co powinna. 
Nawet jeśli yield() spowoduje przejście wątku do puli runnable, 
może on zostać wykonany ponownie, przed innymi wątkami. 
 
 
join() 
------ 
 
Niestatyczna metoda join() pozwala jednemu wątkowi zostać wykonanym, gdy zakończy
się działanie innego wątka. 
 
Thread t = new Thread(); 
t.start(); 
t.join(); 
 
Powyższy przykład dołącza aktualny wątek (main) na koniec wątka, do którego odnosi się "t". 
Blokuje aktualny wątek przed staniem się runnable, dopóki wątek, do którego odnosi
się "t" nie będzie dłużej żywy. 
Występuje także join() z parametrem czasowym - czekaj dopóki wątek "t" nie będzie
zakończony, ale jeśli oczekiwanie trwa dłużej niż np. 5000 milisekund,
przestań czekać i uruchom się mimo wszystko. 
 
 
 
A is running 
A is running 
A is running    Thread b = new Thread(aRunnable); 
A is running -- b.start();    
A is running 
A is running 
B is running 
B is running 
A is running 
B is running 
A is running 
A is running 
B is running 
A is running -- b.join();    // A podłącza się na koniec B 
B is running 
B is running 
B is running 
B is running 
B is running 
B is running 
B is running 
B is running 
B is running 
B is running -- // B zakończył się 
A is running -- // A znów działa 
A is running 
A is running 
A is running 
A is running 
 
 
 
---- 
 
Przypadki, w których wątek będący w stanie running może zmienić stan: 
sleep()    - gwarantuje zatrzymanie wykonywania aktualnego wątku na przynajmniej ustawiony czas 
             (choć może zostać przerwany przed upływem tego czasu); 
 
yield()    - nie gwarantuje się wykonania czegokolwiek, choć typowo powinno
             spowodować przesunięcie 
             aktualnego wątku do puli runnable; 
 
join()     - gwarantuje zatrzymanie aktualnego wątku, dopóki wątek,
             do którego się podłączono nie zakończy się. 
 
 
Inne scenariusze, w których wątek może opuścić stan running: 
 
- metoda run() zakończyła się, 
 
- wywołanie metody wait() na rzecz obiektu (metody wait() nie wywołuje się na rzecz wątku), 
 
- wątek nie może pozyskać lock na rzecz obiektu, którego kod metody chce wykonać, 
 
---- 
 
Nie można zagwarantować, że pojedynczy wątek będzie cały czas w stanie running w trakcie 
operacji atomowej. 
Można jednak zagwarantować, że nawet jeśli wątek wykonujący operację atomową zmieni stan 
z running na inny, żaden inny pracujący wątek nie będzie miał dostępu do tych samych danych. 
 
--- 
 
Synchronizacja i lock 
 
- tylko metody mogą być synchronized, a nie zmienne; 
- każdy obiekt ma tylko jeden lock; 
- nie wszystkie metody w klasie muszą być synchronized; 
- jeśli dwie metody w klasie są synchronized, tylko jeden wątek może mieć dostęp
  do jednej z dwóch metod; 
- jeśli klasa ma zarówno metody synchronized, jak i nie-synchronized, wiele wątków
  może mieć wciąż 
  dostęp do metod nie-synchronized; nie należy oznaczać jako synchronized metod,
  które nie mają dostępu do danych, które chcemy chronić; 
- jeśli wątek wywołuje sleep, wciąż posiada lock; 
- wątek może pozyskać więcej niż jeden lock; np. wątek może wejść do metody
  synchronized (pozyskać lock), a następnie wywołać metodę synchronized innego obiektu; 
  wątek, który posiada lock może także wywołać metodę synchronized tego samego obiektu
  - JVM wie, że ten wątek ma już lock dla tego obiektu; 
- można synchronizować blok instrukcji (synchronized block), a nie koniecznie całą metodę: 
 
  class SyncTest { 
      public void doStuff() { 
          System.out.println("not synchronized"); 
          synchronized(this) { 
              System.out.println("synchronized"); 
          } 
      } 
  } 
 
 
Kiedy wątek uruchamia kod z poziomu synchronizowanego bloku (włączając w to dowolny
kod metody wywołanej z synchronizowanego bloku), mówimy, że kod został uruchomiony
w synchronizowanym kontekście (executing in a synchronized context). 
W przypadku metod synchronized, obiekt używany do wywołania metody jest obiektem,
którego lock musi być pozyskany. 
W przypadku synchronizowania bloku kodu, programista specyfikuje, lock którego obiektu
ma zostać użyty jako lock - można zatem użyć niepowiązanego obiektu,
jako lock dla fragmentu kodu. Daje to możliwość posiadania więcej niż jednego locka
dla kodu synchronizowanego, wewnątrz jednego obiektu.
 
 
Metody statyczne mogą być synchronizowane. Ponieważ jest tylko jedna kopia statycznych danych,
które chcemy ochronić, więc wystarczy tylko jeden lock na klasę, aby synchronizować metody
statyczne.
Każda klasa załadowana ma odpowiadającą instancję java.lang.Class reprezentującą tę klasę. 
Lock instancji java.lang.Class jest używany, żeby zabezpieczyć metodę statyczną klasy
(jeśli jest synchronized). 
 
public static synchronized void getCount() { } 
 
---- 
 
class MyThread extends Thread { 
    StringBuffer sb; 
     
    MyThread(StringBuffer sb) { 
        this.sb = sb; 
    } 
     
    public void run() { 
        synchronized(sb) { 
            for (int i = 0; i < 10; i++) { 
                System.out.print(sb); 
                 
                try { 
                    sleep(100); 
                } catch (InterruptedException e) { } 
            } 
 
            sb.setCharAt(0, (char)(sb.charAt(0) + 1));   
        } 
    } 
} 
 
public class Main { 
    public static void main(String[] args) { 
        StringBuffer sb = new StringBuffer("A"); 
        MyThread t1 = new MyThread(sb); 
        MyThread t2 = new MyThread(sb); 
        MyThread t3 = new MyThread(sb); 
        t1.start(); 
        t2.start(); 
        t3.start(); 
         
        // AAAAAAAAAABBBBBBBBBBCCCCCCCCCC 
    } 
} 
 
---- 
 
Oddaje Lock        Trzyma Lock                                        Class Defining the Method 
------------------------------------------------------------------------------------------------ 
wait()             notify()  (chociaż wątek prawdopodobnie opuści       java.lang.Object 
                              synchronizowany kod wkrótce po tym 
                              wywołaniu i podda swój lock) 
 
                   join()                                               java.lang.Thread 
 
                   sleep()                                              java.lang.Thread 
 
                   yield()                                              java.lang.Thread 
 
 
---- 
 
wait(), notify(), notifyAll() muszą być wywołane z synchronizowanego kontekstu. 
Wątek nie może wywołać wait() lub notify() na rzecz obiektu, chyba że posiada lock tego obiektu. 
 
wait() i notify() są metodami instancyjnymi klasy Object. 
Każdy obiekt ma lock. 
Każdy obiekt może mieć listę wątków, które oczekują na sygnał (notification) od obiektu. 
Wątek dostaje się na tę listę poprzez wywołanie metody wait() obiektu
docelowego (target object). 
Od tego momentu, wątek nie wykonuje żadnej instrukcji dopóki metoda notify()  obiektu docelowego 
nie zostanie wywołana. 
Jeśli wiele wątków oczekuje na ten sam obiekt, tylko jeden będzie wybrany
(bez zagwarantowanej kolejności) do kontynuacji wykonywania się. 
Jeśli nie czeka żaden wątek, żadna akcja nie zostanie podjęta. 
 
Wywołanie wait() powoduje zwolnienie locka, na czas działania wait(). 
 
wait(), notify() i notifyAll() są metodami java.lang.Object. 
sleep() i yield() są metodami statycznymi - jeden obiekt nie może powiedzieć innemu, aby usnął. 
join() i start() nie są metodami statycznymi. 
 
 
Class Object      Class Thread       Interface Runnable 
-------------------------------------------------------- 
wait()            start()            run() 
 
notify()          yield() 
 
notifyAll()       sleep() 
 
                  join() 
-------------------------------------------------------- 
 
 
 
public class Main extends Thread { 
    public static void main(String[] args) { 
        Main m = new Main(); 
        Thread t = new Thread(m);    // OK 
        t.start(); 
    }    
    public void run() { 
        System.out.println("aaa"); 
    } 
} 
 
Klasa Thread implementuje interfejs Runnable - Thread może wziąć obiekt typu Thread,
jako argument konstruktora. 
w3cw3c
automatyka przemysłowa