Podstawy Programowania w języku Java
Programowanie obiektowe
Klasy, pola, metody, pola i metody statyczne, pakiety
Podstawy Programowania w języku Java
Obiektowość
Podstawy Programowania w języku Java
Obiektowość
Czym jest programowanie obiektowe?
Zacznijmy od samej idei programowania obiektowego. Java od samego początku była obiektowym językiem programowania. Ale co dla nas oznacza?
Programowanie obiektowe to paradygmat programowania, który opiera się na założeniu, że tworzone przez nas aplikacje mają być wykonane w taki sposób, aby jak najlepiej odzwierciedlały otaczającą nas rzeczywistość, zaś dane, które będą prezentowane za pośrednictwem tej aplikacji, miały odzwierciedlać sposób, w jaki ludzie postrzegać te dane.
Najprostszym przykładem może być np. to, że jabłko to owoc, jakiś produkt marki Volvo to samochód i tak dalej.
Podstawy Programowania w języku Java
Obiektowość
Klasa a obiekt
W programowaniu obiektowym mamy do czynienia z obiektami, które możemy traktować jako agregaty pewnych informacji wraz z przypisanymi im dowolnymi akcjami do wykonania. Obiekt jest w takim razie specyficznym bytem, który wyróżnia się na tle opisywanego przez nas fragmentu rzeczywistości i który posiada pewne atrybuty.
Klasa zaś to właśnie opis tych wspólnych cech grup podobnych obiektów, które są niezmienne dla tych grup obiektów. Innymi słowy, jest to zestaw atrybutów i akcji, które można wykonać na tych obiektach.
Każdy obiekt klasy może zawierać niektóre informacje w postaci pól, które to mają ustalone typy i nazwy.
Akcje są reprezentowane przez metody, które operują na obiektach.
Metody mogą zwracać informacje o stanie obiektu, modyfikować go itp. Są zawsze wywoływane na konkretnym obiekcie i mają bezpośredni dostęp do wszystkich pól tego konkretnego obiektu, na rzecz którego zostały wywołane.
Pola i metody klasy są zbiorczo nazywane jej niestatyczni elementami, ponieważ istnieją również elementy statyczne: pola statyczne i statyczne metody.
Te dwa pojęcia są od siebie nierozłączne - obiekt i klasa. Obiekt tworzony jest na bazie klasy, a klasa opisuje, co obiekt potrafi oraz jakie posiada właściwości i cechy.
Podstawy Programowania w języku Java
Obiektowość
Podsumowując
Klasy definiują nowe typy i mogą zawierać:
-
pola (niestatyczne)
-
pola statyczne
-
konstruktory
-
metody (niestatyczne)
-
metody statyczne
-
statyczne i niestatyczne bloki inicjalizacyjne
-
definicje innych klas (tzw. klasy wewnętrzne) oraz enumeratory
Podstawy Programowania w języku Java
Obiektowość
Obiekty
Obiekty (instancje) klas są zawsze tworzone na stercie (w wolnej pamięci). Są one zawsze anonimowe — nie ma możliwości nadania nazwy samemu obiektowi. Nie można również utworzyć obiektu lokalnie na stosie — lokalne mogą być tylko tworzone referencje, którym to własnie nadajemy nazwy.
Operator new tworzy nam obiekt i zwraca referencję do tworzonego obiektu, którą możemy przechowywać w nazwanej zmiennej referencyjnej.
Jeśli nie ma przechowywanych żadnych referencji do obiektu, to tak pozostawiony obiekt może zostać usunięty przez podproces modułu wyrzucania elementów bezużytecznych JVM - Garbage Collector (chociaż nie wiemy, czy i kiedy to nastąpi).
Podstawy Programowania w języku Java
Obiektowość
Przykład definicji nowego typu (klasy), który opisuje punkty na płaszczyźnie
package pl.edu.pja.sladan;
public class Point {
//fields
private int x;
private int y;
private int pointNumber;
//private field
private static int counter = 0;
{ // nonstatic initialization block
System.out.println("nonstatic initialization block");
}
static { // static initialization block
System.out.println("static initialization block");
}
//constructor
public Point(int x, int y){
this.x = x;
this.y = y;
pointNumber = counter++;
System.out.println("Constructor");
}
public Point(){
this(0, 0);
}
//getters
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getPointNumber() {
return pointNumber;
}
//setters
public void setX(int x) {
this.x = x;
}
public void setY(int y) {
this.y = y;
}
//static method
public static int getCounter() {
return counter;
}
//overrided method toString
@Override
public String toString() {
return "Punkt nr " + getPointNumber() + " (" + getX() + ";" + getY() + ")";
}
}
Podstawy Programowania w języku Java
Obiektowość
Hermetyzacja
W ramach klas, musimy mieć zapewniony mechanizm ochrony elementów przed niepożądanym dostępem z poziomu innych klas/metod. Taki mechanizm nazywamy hermetyzacją lub enkapsulacją.
Nad dostępnością mamy kontrolę dodając na początku ich deklaracji odpowiednie słowo kluczwe:
-
private - Pole/metoda/konstruktor są dostępne tylko z poziomu innej metody tej samej klasy
-
protected - Pole/metoda/konstruktor są dostępne z poziomu innej metody tej samej klasy, z metod wszystkich klasy należących do tego samego pakietu, a także z klas, które dziedziczą po niej nawet jeśli należą do innego pakietu.
-
default (inaczej nazywany jako package private) - uzyskiwany, gdy nie określiny żadnego innego specyfikatora dostępu. Pole/metoda/konstruktor są dostępne z poziomu innej metody tej samej klasy, a także z metod wszystkich klasy należących do tego samego pakietu
-
public - pole/metoda/konstruktor są dostępne z poziomu metod wszystkich klas, w których widoczna jest nasza klasa
Zaleca się, aby deklarować wszystkie pola jako private (lub co najmniej protected), jendak wtedy musimy pamiętać, aby zrealizować dostęp do wartości i/lub modyfikację pola za pośrednictwem metod typu get i set, które pozwalają na spójny i bezpieczny sposób dostepu/zmiany wartości z uwzględnieniem wszelkich wymaganych kryteriów.
Podstawy Programowania w języku Java
Obiektowość
Pola
//fields
private int x;
private int y;
private int pointNumber;
//private field
private static int counter = 0;
Pola x, y oraz pointNumber są polami niestatycznymi. Każdy obiekt będzie zawierał "własny" zestaw tych pól i oczywiście ich wartości mogą być różne dla każdego obiektu z osobna.
Pole counter jest polem statycznym, co oznacza, że zmienna ta, będzie utworzona raz, zaś referencja do niej będzie osigalna z poziomu każdego obiektu jak i samej klasy, bez potrzeby posiadania obiektu. Wartość w takim polu jest "współdzielona", a więc każda zmiana wykoanana na taim polu będzie widoczna z poziomu każdej innej instancji klasy. W tym przypadku wykorzystujemy pole statyczne jako licznik utworzonych obiektów, który równocześnie umożliwia nam realizację prostej autonumeracji obiektów. Statyczne pola naszej klasy są powoływane do istnienia i inicjowane, gdy klasa jest ładowane przez JVM (jeśli klasa jest enumeratorem, nastąpi to po stworzeniu wszystkich wartości enumeratora).
Podstawy Programowania w języku Java
Obiektowość
Konstruktory
//constructor
public Point(int x, int y){
this.x = x;
this.y = y;
pointNumber = counter++;
System.out.println("Constructor");
}
Konstruktory pełnią ważną rolę w procesie fabrykacji obiektu. Mają one dostęp do wszystkich składowych obiektów tworzonych na podstawie danej klasy. Konstruktor jest bardzo specyficznym bytem, który jest ograniczony pewnymi kryteriami:
-
nazwa konstruktora musi być taka sama jak nazwa klasy
-
nie deklaruje żadnego typu zwracanego (nawet nie void)
-
zostanie on automatycznie wywołany na samym końcu procesu konstruowania obiektu
-
nie ma możliwości wywołania konstruktora na obiekvie, który już istnieje
Zwykle konstruktory są wykorzystywane do inicjalizacji pól tworzonego obiektu konkretnymi wartościami (przekazanymi przez argumenty lub wartościami domyślnymi). Konstruktory jednak mogą wykonać dowolne zadanie, jakie im powierzymy.
Aby użyć konkretnego konstruktora podczas tworzenia obiektu, po prostu piszemy po słowie kluczowym new i nazwie klasy, stosowne argumenty zgodnie z określoną w konstruktorze licznością i kolejnością typów.
Podstawy Programowania w języku Java
Obiektowość
W klasie można zdefiniować wiele metod i konstruktorów o tej samej nazwie (przeciążenie) (w przypadku konstruktorów nie da się tego uniknąć, ponieważ konstruktory muszą być nazwane zgodnie z nazwą klasy). Przeciążone metody i konstruktory muszą różnić się między sobą liczbą parametrów i/lub typami tych parametrów, dzięki czemu kompilator patrząc na argumenty wie którą z metod ma wykonać.
Jeśli nie zdefiniujemy żadnego konstruktora (i tylko wtedy), kompilator doda konstruktor domyślny — bez parametrów i nic nie robiący. Wszystkie pola również zostaną zainicjowane wartościami domyślnymi: zero dla liczb typy, null dla referencji, false dla logicznych. Konstruktor bez parametrów, niezależnie od tego, czy jest tworzony przez kompilator, czy zdefiniowany przez nas, nazywany jest konstruktorem domyślnym.
//constructor
public Point(int x, int y){
this.x = x;
this.y = y;
pointNumber = counter++;
System.out.println("Constructor");
}
public Point(){
this(0, 0);
}
Każdy konstruktor może wywołać (tylko w swoim pierwszym wierszu) inny konstruktor tej samej klasy z wykorzystaniem słowa kluczowego this. Taki konstruktor jest nazywany konstruktorem delegującym. Po wykonaniu innego konstruktora przepływ sterowania powraca do konstruktora wywołującego (delegującego).
Podstawy Programowania w języku Java
Obiektowość
Metody
//getters
public int getX() {
return x;
}
//...
//setters
public void setX(int x) {
this.x = x;
}
//...
public static int getCounter() {
return counter;
}
@Override
public String toString() {
return "Punkt nr " + getPointNumber() + " (" + getX() + ";" + getY() + ")";
}
W ramach naszych klas możemy tworzyć merówno metody zwykłe jak i metody statyczne. Szczególnym typem metod są gettery i settery, które umożliwiają nam kontrolowane zwrócenie użytkownikowi oraz modyfikację naszych pól klasy. W kontekście metod, możemy w nich zdefiniować dowolną funkcjonalność, z założeniem, że metody zwykłe mają dostęp do pól klasy i mogą na nich operaować. W przypadku metod statycznych nie mamy dostępu do pól niestetycznych z racji tego, że metoda ta nie jest powiązana z żadnym konkretnym obiektem, a ogólnie z klasą, tak więc dostęp ma jedynie do innych statycznych własciwości.
Inną typową realizacją jest przesłonięta metoda toString. Jest to metoda, która oryginalnie jest zdefiniowana w klasie Object (każda klasa dziedziczy po tej specjalnej klasie). Metoda toString jest wywoływana automatycznie w każdym momencie, gdy mamy do czynienia z obiektem, a potrzebny jest napis go opisujący (np. metoda toStirng zostanie wywołana automatycznie przez funkcję println). Realizacja tej metody w oryginalnej implementacji z klasy Object zwraca nam napis w postaci: nazwaKlasy@HashCode.
Podstawy Programowania w języku Java
Obiektowość
Bloki inicjalizacyjne
{ // nonstatic initialization block
System.out.println("nonstatic initialization block");
}
static { // static initialization block
System.out.println("static initialization block");
}
Wewnątrz definicji klasy możemy również umieścić statyczne i niestatyczne bloki inicjalizacyjne. Są to bloki kodu ujęte w nawiasy klamrowe (w przypadku statycznego bloku inicjującego poprzedzany blok słowem kluczowym static). Takie bloki znajdują się w obrębie klasy, poza wszelkimi metodami.
Ciało niestatycznego bloku inicjującego zostanie wykonane w procesie tworzenia dowolnego obiektu po utworzeniu samego obiektu i po zainicjowaniu jego elementów wartościami domyślnymi, ale przed wykonaniem konstruktora, tak więc możemy tutaj umieścić kod, który powinien zostać wykonany na początku dowolnego przeciążonego konstruktora.
Statyczny blok inicjujący jest wykonywany tylko raz: gdy klasa jest ładowana przez JVM (po utworzeniu stałych wyliczeniowych, jeśli klasa jest enumeratorem). Jeśli klasa zawiera również pola statyczne, najpierw następuje inicjalizacja pól statycznych, a następnie wykonywane są statyczne bloki inicjalizacyjne w kolejności, w jakiej pojawiają się w definicji.
Podstawy Programowania w języku Java
Obiektowość
W ten sposób dla poniższej metody main:
public static void main(String[] args) {
Point point = new Point();
}
Kolejność wykoniania z uwzględnieniem bloków inicjalizacyjnych będzie następuący:
static initialization block
nonstatic initialization block
Constructor
Podstawy Programowania w języku Java
Pakiety
Podstawy Programowania w języku Java
Pakiety
Pakiety
Wraz z realizacją coraz to większych projektów oraz narastającą liczbą tworzonych klas, należy zacząć dbać o zarządzanie plikami i ich segregację. Szczególnie ma to duże znaczenie w przypadku projektów, gdzie do czynienia nie będziemy mieli z kilkoma/kilkunastoma klasami, a kilkoma tysiącami. Wyobraźmy sobie, że programista musi rozwijać taką aplikację, gdzie bezpośrednio w katalogu src ma umieszczone kilka tysięcy plików z kodami źródłowymi. Większość czasu programista spędzi wtedy na wyszukiwaniu odpowiednich klas, przez co wydajność samego procesu twórczego jest mocno ograniczona.
W związku z powyższym rozwiązaniem tego typu problemów są pakiety. Jest to nic innego, jak z punktu widzenia samego systemu operacyjnego, segregacja i pogrupowanie (np ze względu na funkcjonalność) plików w postaci odpowiedniej struktury katalogów. Z punktu widzenia języka programowania pakiety tworzą poza pogrupowaniem klas/interfejsów/enumeratorów również "adresy" pozwalające na zlokalizowanie naszych klas w trakcie działania aplikacji.

package pl.edu.pja.sladan;
W pliku źródłowym może znajdować się wyłącznie jedna linia z pakietem. Musi znajdować się na początku pliku. Nie jest to linia obowiązkowa, jednak używanie domyślnego pakietu nie jest dobrą praktyką. W powyższej linijce deklaracji pakietu możemy wyszczególnić:
-
package - słowo kluczowe informujące w jakim pakiecie znajduje się analizowana klasa
-
pl.edu.pja.sladan - właściwa nazwa pakietu. Przyjęło się, że za pakiet używa się odwróconej nazwy domeny. Takie podejście pozwala na łatwiejsze uniknięcie konfliktów w kontekście wielu niezależnych projektów. Istotne jest tutaj odwzorowanie struktury katalogów. Każdy człon pakietu odpowiada katalogowi na dysku. Nazwy pakietów powinny odwzorowywać ich zawartość.
Programowanie obiektowe i Graficzne Interfejsy Użytkownika (GUI)