Podstawowe pojęcia programowania obiektowego i Javy
Język Java jest Zorientowany Obiektowo, czyli posługuje się pojęciami takimi jak Obiekt, czy Klasa. Poniżej przedstawiamy listę podstawowych pojęć związanych z programowaniem obiektowym. Nie są to ścisłe definicje jakie można znaleźć w książkach. Ich zadaniem jest opisanie w przystępny sposób pojęć związanych z OOP.
Klasa
Abstrakcyjny byt określający zbiór Obiektów o takich samych właściwościach.
Klasa definiuje zestaw Metod i Pól dla swoich Obiektów. Przykładem klasy może być samochód. Może on podejmować różne działania np. jechać i ma pewne właściwości np. kolor. Jest jednak abstrakcyjny, mówiąc inaczej "nienamacalny". Jeżeli mówimy o klasie Samochód to oznacza, że mówimy o jakimś samochodzie.
Ogólny wzór definicji klasy w Javie wygląda w następujący sposób:
[modyfikator dostępu] [abstract] class NazwaKlasy{
// definicja pól i metod
}
// definicja pól i metod
}
Nasz wzorcowy samochód:
public class Samochod{
public Color kolor = new Color(255,255,255); // pole kolor określa kolor samochodu
public void jedzie(){
//kod odpowiedzialny za jazdę samochodu
}
}
public Color kolor = new Color(255,255,255); // pole kolor określa kolor samochodu
public void jedzie(){
//kod odpowiedzialny za jazdę samochodu
}
}
Klasa może implementować Interfejs. Oznacza to, że klasa ma wszystkie metody, zachowania, definiowane przez interfejs. Klasa musi implementować wszystkie metody interfejsu. Tu jednak rodzi się pytanie co zrobić jeżeli nie chcemy implementować wszystkich metod? Można oczywiście zaimplementować je w taki sposób by zwracały wartości null. Jednak nie jest to dobre rozwiązanie. Można powiedzieć nawet więcej, jest to najgorsze z możliwych rozwiązań, ponieważ ukrywa fakt braku implementacji metod. Znacznie lepszym rozwiązaniem jest uczynienie danej klasy abstrakcyjną.
Klasa abstrakcyjna zawiera w sobie zwykłe metody, czyli takie które mają jawną implementację, oraz metody abstrakcyjne. Metoda abstrakcyjna nie ma implementacji. Poniżej przykład klasy abstrakcyjnej:
public abstract class KlasaAbstrakcyjna {
public int pole;
public abstract void metodaAbstrakcyjna();
public void metodaZwykła(){
//Kod metody
}
}
public int pole;
public abstract void metodaAbstrakcyjna();
public void metodaZwykła(){
//Kod metody
}
}
Po co to wszystko? Dochodzimy do bardzo ważnego elementu programowania obiektowego jakim jest dziedziczenie. Załóżmy, że chcemy stworzyć klika klas odpowiadających różnym markom i typom samochodów. Interesuje nas to że samochody jeżdżą i mogą mieć doczepioną naczepę. Najbardziej prawidłowe podejście powinno wyglądać w następujący sposób:
- Definiujemy interfejs Samochód. Posiada on metody które odpowiadają działaniom wszystkich samochodów np.
- jedź
- załóż naczepę
- Tworzymy klasę abstrakcyjną, która implementuje interfejs Samochód w taki sposób, że metoda jedź jest wspólna dla wszystkich samochodów, a metoda załóż naczepę abstrakcyjna.
- Tworzymy poszczególne klasy samochodów. Dziedziczą, rozszerzają, one klasę AbstrakcyjnySamochód i implementują zakładanie naczepy w zależności od potrzeb.
Nasze samochody to:
Ferrari
Star
Kod takiego programu powinien wyglądać mniej więcej w taki sposób:
// plik Samochód
public interface Samochód {
public void jedź();
public void załóżNaczepę();
}
//plik AbstrakcyjnySamochód
public abstract class AbstrakcyjnySamochód implements Samochód {
public void jedź() {
//kod odpowiedzialny za poruszanie się samochodu
}
public abstract void załóżNaczepę();
}
//plik Ferrari
public class Ferrari extends AbstrakcyjnySamochód {
public void załóżNaczepę() {
//nic nie robim. Ferrari nie może mieć naczepy
}
}
//plik Star
public class Star extends AbstrakcyjnySamochód {
public void załóżNaczepę() {
//Czynności związane z zakładaniem naczepy
}
}
public interface Samochód {
public void jedź();
public void załóżNaczepę();
}
//plik AbstrakcyjnySamochód
public abstract class AbstrakcyjnySamochód implements Samochód {
public void jedź() {
//kod odpowiedzialny za poruszanie się samochodu
}
public abstract void załóżNaczepę();
}
//plik Ferrari
public class Ferrari extends AbstrakcyjnySamochód {
public void załóżNaczepę() {
//nic nie robim. Ferrari nie może mieć naczepy
}
}
//plik Star
public class Star extends AbstrakcyjnySamochód {
public void załóżNaczepę() {
//Czynności związane z zakładaniem naczepy
}
}
Kilka uwag o dziedziczeniu i interfejsach w javie:
- Można dziedziczyć tylko po jednej klasie
- Klasa może implementować kilka interfejsów, ale trzeba uważać na konflikty nazw
- Interfejs może dziedziczyć po innym interfejsie
- Wszystkie obiekty dziedziczą po klasie Object
Obiekt
Byt fizyczny stanowiący instancję klasy
Obiekt jest fizyczną "manifestacją" Klasy. Oznacza to że ma własne miejsce w pamięci komputera i możemy nim manipulować. Jeżeli mówimy o obiekcie samochód będącym instancją Klasy Samochód to oznacza, że mówimy o konkretnym samochodzie.
Obiekt definiujemy jako Zmienną.
Zmienna
Byt fizyczny o którym mówimy że ma wartość
Przez pojęcie zmiennej w Javie rozumiemy referencję, wskazanie, do określonego miejsca w pamięci komputera. Zmienne reprezentują dane i pozwalają za swoim pośrednictwem nimi manipulować.
Stała
Rodzaj zmiennej, której wartość nie podlega modyfikacji
Stała to zmienna, która po nadaniu jej wartości nie może być już zmieniona. Zazwyczaj tej konstrukcji używa się do definiowania np. ustawień. Chcąc uzyskać taki efekt należy użyć słowa kluczowego final
Metoda
Działanie, które mogą podjąć Obiekty danej Klasy
Metoda określa Działanie. Dlatego najczęściej stosuje się do jej nazwania czasowniki. samochód.jedzie() - jedzie() to metoda, działanie. Metody wywołujemy za pomocą znaku . po nazwie zmiennej.
Pole
Właściwość Obiektu danej Klasy
Samochód może mieć kolor. Pole Klasy rozumiemy właściwość lub relację MA. Pole jest zmienną która należy do klasy.
Interfejs
Dobrze zdefiniowany zestaw czynności - metod, które może wykonać obiekt danej klasy
Interfejs jest jednym z najtrudniejszych do zrozumienia pojęć OOP. Jednocześnie jest stosunkowo prostym pojęciem jeżeli posłużymy się przykładem. Załóżmy że Samochód Jest Interfejsem. Oznacza to że możemy korzystać z jego Metod i Stałych. Jednocześnie możemy zdefiniować kilka różnych klas implementujących interfejs Samochód. Wszystkie one będą miały dobrze zdefiniowane metody interfejsu. Oznacza to że jeżeli wiemy że dany obiekt implementuje interfejs samochód to możemy "w ciemno" używać metod tego interfejsu. Nie interesuje nas jak one działają, a jedynie ich parametry wywołania i to co otrzymamy w wyniku ich działania.
W życiu codziennym spotykamy się z interfejsami które zapewniają funkcjonalności, a jednocześnie nie musimy znać zasad ich działania. Przykład to pilot od telewizora. Jest interfejsem pozwalającym na włączania, zmianę kanałów i wyłączanie urządzenia.
W języku Java definicja jest następująca:
[modyfikator dostępu] interface NazwaInterfejsu{
// definicja stałych i metod
}
// definicja stałych i metod
}
W naszym przykładzie z samochodem:
public interface Samochod{
public final Color kolor = new Color(0,0,0); // pole będące stałą. Produkujemy Fordy T ;)
public void jedzie();
}
public final Color kolor = new Color(0,0,0); // pole będące stałą. Produkujemy Fordy T ;)
public void jedzie();
}
Modyfikatory dostępu
Od początku zarówno w definicji klasy jak i interfejsu, metody i pola znajduje się tajemniczy zapis [modyfikator dostępu].
Modyfikator dostępu określa w jaki sposób inne obiekty mogą otrzymać dostęp do danego pola, metody, definicji klasy czy interfejsu. W języku Java wyróżniamy cztery modyfikatory dostępu: publiczny, prywatny, chroniony i domyślny.
Modyfikator publiczny
Określany jest słowem kluczowym public. Oznacza iż do danej metody, pola, definicji klasy i interfejsu ma dostęp każdy obiekt w uniwersum javy (JU). Dostęp ten nie jest uzależniony o tego w jakim pakiecie znajduje się udostępniana własność i czy to jest ten sam pakiet co pakiet obiektu z którego pochodzi żądanie.
Modyfikator prywatny
Jest przeciwieństwem modyfikatora publicznego. Pola, metody, klasy i interfejsy oznaczone słowem private są całkowicie niewidoczne poza definicją swojego właściciela. Przykładowa klasa zawierająca zarówno pola, metody, klasy i interfejsy prywatne:
class PrywatnaKlasa {
private int polePrywatne;
private void metodaPrywatna(){}
private interface prywatnyInterfejs{}
private class prywatnaKlasa implements prywatnyInterfejs{}
}
private int polePrywatne;
private void metodaPrywatna(){}
private interface prywatnyInterfejs{}
private class prywatnaKlasa implements prywatnyInterfejs{}
}
Żaden obiekt w JU nie będzie wstanie odwołać się do zawartości PrywatnaKlasa. Bardzo istotnym faktem jest to, że klasa znajdująca się na najwyższym poziomie, czyli PrywatnaKlasa nie może być prywatna.
Modyfikator chroniony
Modyfikator chroniony oznaczamy słowem kluczowym protected. Elementy oznaczone w ten sposób są widoczne tylko dla innych obiektów znajdujących się poniżej w tej samej hierarchii klas. Innymi słowy dziedziczące po klasie zawierającej elementy chronione. Oczywiście są też widoczne dla innych elemntów znajdujacych się w tym samym pakiecie i pod pakietach.
Modyfikator domyślny
Nie jest oznaczony żadnym słowem kluczowym. Nie zrozumienie jak działa modyfikator domyślny jest źródłem popularnych błędów związanych z próbą wykorzystania oznaczonych w ten sposób pól i metod. Modyfikator domyślny można zdefiniować w następujący sposób, jeżeli dostęp do pola, metody, klasy lub intrerfejsu jest oznaczony jako domyślny to mogą go otrzymać tylko te obiekty, które znajduja się w tym samym pakiecie. Oznacza to, że klasy dziedziczące po klasie zawierającej elementy z dostępem domyślnym nie będą mogły użyć tego elementu chyba, że znajdują się w tym samym pakiecie. Ten sam pakiet oznacza literalnie ten sam katalog w strukturze pakietów. Nie może to być np. podkatalog o innej strukturze pakietów nie wspominając.
Inne modyfikatory
Poza modyfikatorami dostępu funkcje i pola obiektów mogą być opisane za pomocą innych modyfikatorów.
Modyfikator final
Pole obiektu oznaczone jako final po inicjacji nie może być modyfikowane. Oznacza to, że inicjacji pola można dokonać tylko na dwa sposoby. Poprzez jawne inicjowanie w definicji klasy:
lub w konstruktorze:
class Klasa{
final Object pole;
public Klasa(){
pole = new Object();
}
public Klasa(Object obj){
pole = obj;
}
}
final Object pole;
public Klasa(){
pole = new Object();
}
public Klasa(Object obj){
pole = obj;
}
}
Wartość pola po zainicjowaniu nie może być już zmieniana. Jeżeli pole wskazuje na jakiś obiekt to dalej można używać metod ustawiających tego obiektu i bezpośrednich odwołań do pól do ustawiania wartości pól tego obiektu.
W przypadku metody oznaczenie jej jako final powoduje, że klasy dziedziczące po klasie nie mogą przesłonić metody:
public class KlasaA {
final void metoda(){}
}
class KlasaB extends KlasaA{
// Niedozwolone! Nie można przesłonić metody final
//void metoda(){}
}
final void metoda(){}
}
class KlasaB extends KlasaA{
// Niedozwolone! Nie można przesłonić metody final
//void metoda(){}
}
Jezeli klasa jest oznaczona jako final to nie można rozszerzyć tej klasy:
public final class KlasaA {
}
// nie można rozszerzyć klasy oznaczonej jako final
//class KlasaB extends KlasaA{}
}
// nie można rozszerzyć klasy oznaczonej jako final
//class KlasaB extends KlasaA{}
Modyfikator static
Modyfikator static oznacza iż pole obiektu ma taką samą wartość dla wszystkich obiektów danej klasy. Formalnie oznacza to iż wszystkie obiekty danej klasy odwołują się do tego samego miejsca w pamięci.
Jeżeli metoda jest oznaczona jako statyczna to może być wywołana bez potrzeby tworzenia obiektu klasy definiującej tą metodę. W tym miejscu należy wspomnieć iż do zarówno metod, jak i pól oznaczonych jako static dobieramy się w inny sposób iż do normalnych metod i pól. Chcąc odwołać się do statycznego elementu należy użyć wzorca:
<<nazwa_klasy>>.<<nazwa_metody/nazwa_pola>>
Jeżeli chcemy użyć tradycyjnego odwołania:
obiekt.metoda();
obiekt.pole;
obiekt.pole;
to kompilator zwróci ostrzeżenie The static field/method obiekt.pole/obiekt.metoda() should be accessed in a static way.
Program powinien kompilować się bez błędów i ostrzeżeń.
Modyfikator strictfp
Metoda lub klasa oznaczona w ten sposób będzie wykonywana tak by wszystkie obliczenia były zgodne ze standardem IEEE-754.
Modyfikator native
Zaawansowany modyfikator, którym oznaczane są metody wykonywane przez JNI (Java Native Interface). W praktyce oznacza to, że metody te są implementowane w języku innym niż Java, a JVM wywołując je odwołuje się do mechanizmów pozwalających na komunikację z np C++.
Modyfikator volatile
Zaawansowany modyfikator związany z JPA (Java Persistence Api). Pola oznaczone w ten sposób nie są utrwalane.
Typy Proste i Obiekty
Typy danych obecne w Javie dzielą się na typy proste:
Nazwa typu | Liczba bajtów | Dopuszczalne wartości | Znaczenie | Przykłady literałów |
byte | 1 | 127 | l. całkowita | 1, 01, 0x01 |
short | 2 | 32768 | l. całkowita | 128, 0xFF |
int | 4 | 2147483648 | l. całkowita | 32768, 0x1000 |
long | 8 | 9223372036854775808 | l. całkowita | 3l(L), 21474836 |
float | 4 | 3.xE+38 | l. rzeczywiste | 3f, 3F, 3e(E)+10 |
double | 8 | 1.xE+30 | l. rzeczywiste | 0.3, 0.3d(D) ... |
char | 2 | -0...65556 | znaki Unicodu | 'a', \\u0013 |
boolean | 1 | true/false | wartości logiczne | true, false |
Wszystkie inne dane w Javie traktowane są jako obiekty.
przykłady deklaracji zmiennych:
int a; //deklaracja zmiennej a typu całkowitego
char b; //deklaracja zmiennej b, która zawiera znaki Unicode
char b; //deklaracja zmiennej b, która zawiera znaki Unicode
Równoczesna deklaracja i inicjacja zmiennych:
int a = 3;
char b = 'c';
char b = 'c';
Oprócz typów prostych w Javie istnieją typy obiektowe. Są to np:
Button, Panel, Label, Okno - nazwa klasy stworzonej przez użytkownika
Deklaracja zmiennych typu obiektowego:
Typy proste jak i obiekty mogą zostać najpierw zdefiniowane, a zainicjowane dopiero później. Zmienne które są polami obiektów zawsze są inicjowane z wartością domyślną. Zmienne lokalne (zwane też automatycznymi) muszą zostać jawnie zainicjowane. Przykład:
class Foo{
int zmienna; //zmienna będzie miała domyślna wartość - 0 - w momencie stworzenia obiektu klasy
Object obj; //zmienna będzie miała domyślna wartość - null - w momencie stworzenia obiektu klasy
pubic void bar(){
/* zmienna musi zostać jawnie zainicjowania,
* inaczej kompilator zwróci błąd variable may not been initialized
* jeżeli będziemy chcieli jej użyć
*/
int zmienna2;
zmienna2 = 1;
System.out.println(zmienna); // 0
System.out.println(zmienna2); // 1
}
}
int zmienna; //zmienna będzie miała domyślna wartość - 0 - w momencie stworzenia obiektu klasy
Object obj; //zmienna będzie miała domyślna wartość - null - w momencie stworzenia obiektu klasy
pubic void bar(){
/* zmienna musi zostać jawnie zainicjowania,
* inaczej kompilator zwróci błąd variable may not been initialized
* jeżeli będziemy chcieli jej użyć
*/
int zmienna2;
zmienna2 = 1;
System.out.println(zmienna); // 0
System.out.println(zmienna2); // 1
}
}
Operatory
W javie istnieje kilka operatorów. Możemy je podzielić na trzy główne grupy. Pierwszą stanowią operatory matematyczne. Drugą operatory logiczne, w tym operatory porównania, a trzecią operatory bitowe.
Operatory matematyczne
Operator | Znaczenie |
---|---|
+ | Dodawanie |
- | Odejmowanie |
* | Mnożenie |
/ | Dzielenie |
% | Reszta z dzielenia |
\ | Wynik dzielenia z resztą |
Dodatkowo zdefiniowane są jeszcze dwa operatory inkrementacji i dekrementacji:
Operator | Znaczenie |
---|---|
++ | Inkrementacja - zwiększenie o 1 |
-- | Dekrementacja - zmniejszenie o 1 |
Isntnieją też operatory mające sens "wykonaj działanie i przypisz". Za przykład niech posłuży nam operator dodawania:
int a = 1;
a += 1; // a ma wartość 2
a += 1; // a ma wartość 2
Operatory logiczne
Komputer jest maszyną "myślącą" w sposób całkowicie logiczny. Na podstawowym poziomie komputer wykonuje najprostsze operacje logiczne. Zasada te ma odwzorowanie w operatorach logicznych oraz w operatorach porównania.
Operator | Znaczenie |
---|---|
|| | Operacja ∨ - LUB |
$$ | Operacja ∧ - I |
! | Operacja ~ - negacja |
> | większy niż |
< | mniejszy niż |
>= | większy równy |
<= | mniejszy równy |
!= | różny od |
Jak widać w powyższej tabeli zabrakło ważnego operatora. Operator równy w języku Java sprawia osobą początkującym wiele problemów. Wynika to ze specyfiki języka. Zapis:
a == b;
ma różne znaczenie w zależności od tego czy odnosi się do typów prostych czy też obiektów. Jeżeli porównujemy w ten sposób dwie zmienne typu prostego to operator działa "intuicyjnie". Przykład:
W odniesieniu do zmiennych obiektowych zasada ta jest inna. Operator == oznacza Identyczność, a nie równość:
Integer a = new Integer(1);
Integer b = new Integer(1);
Integer c = a; // c jest referencją do tego samego obiektu na stercie co a
System.out.println(a == b); // zwróci flase, a i b to różne obiekty tej samej klasy
System.out.println(a == c); // zwróci true, a i c to ten sam obiekt
Integer b = new Integer(1);
Integer c = a; // c jest referencją do tego samego obiektu na stercie co a
System.out.println(a == b); // zwróci flase, a i b to różne obiekty tej samej klasy
System.out.println(a == c); // zwróci true, a i c to ten sam obiekt
Jeżeli chcemy porównać dwa obiekty należy użyć metody equals():
Integer a = new Integer(1);
Integer b = new Integer(1);
System.out.println(a.equals(b)); // zwróci true
Integer b = new Integer(1);
System.out.println(a.equals(b)); // zwróci true
Ostatnim operatorem porównania dla obiektów jest słowo kluczowe instanceof. Przykład:
Integer a = new Integer(1); // dziedziczy po Number, a ta po Object
System.out.println(a instanceof Integer ); // zwróci true a jest klasy Integer
System.out.println(a instanceof Object ); // zwróci true, klasa Integer dziedziczy po Object, a jest klasy Object
System.out.println(a instanceof Serializable ); // zwróci true, klasa Number implementuje Serializable, a jest Serializable
System.out.println(a instanceof Integer ); // zwróci true a jest klasy Integer
System.out.println(a instanceof Object ); // zwróci true, klasa Integer dziedziczy po Object, a jest klasy Object
System.out.println(a instanceof Serializable ); // zwróci true, klasa Number implementuje Serializable, a jest Serializable
90% błędów popełnianych przez początkujących programistów związanych z warunkami logicznymi związane jest z niezrozumieniem i pomyleniem operatora == i metody equals();
Operatory bitowe
Komputery pracują na bitach. Operacje na nich w wielu przypadkach pozwalają na znaczne przyspieszenie obliczeń.
Operator | Znaczenie |
---|---|
| | Operacja LUB |
$ | Operacja I |
~ | Operacja negacji |
^ | Operacja różnicy symetrycznej XOR |
>> | Operacja przesunięcia w prawo |
<< | Operacja przesunięcia w lewo |
>>> | Operacja przesunięcia w prawo z wypełnieniem zerami |
Instrukcje sterujące
W momencie gdy chcemy, aby program dokonał wyboru jednej z dróg na podstawie prawdziwości jakiegoś warunku logicznego możemy użyć jednej z dwóch instrukcji sterujących. Jeżeli chcemy, aby wybór został dokonany na podstawie stanu obiektu możemy użyć przełącznika - switch.
Instrukcja if / if else
Najprostszą instrukcją warunkową jest instrukcja if:
if(warunek_logiczny){
//instrukcje wykonane jeżeli warunek jest PRAWDZIWY
}
//instrukcje wykonane jeżeli warunek jest PRAWDZIWY
}
odmianą tej instrukcji jest instrukcja if else:
if(warunek_logiczny){
//instrukcje wykonane jeżeli warunek jest PRAWDZIWY
}
else{
//instrukcje wykonane jeżeli warunek jest FAŁSZYWY
}
//instrukcje wykonane jeżeli warunek jest PRAWDZIWY
}
else{
//instrukcje wykonane jeżeli warunek jest FAŁSZYWY
}
instrukcje można zagłębiać:
if(warunek_logiczny1){
if(warunek_logiczny2){
//instrukcje wykonane jeżeli warunek2 jest PRAWDZIWY
}
}
if(warunek_logiczny2){
//instrukcje wykonane jeżeli warunek2 jest PRAWDZIWY
}
}
oraz dokonywać wielokrotnego wyboru:
if(warunek_logiczny1){
//instrukcje wykonane jeżeli warunek1 jest PRAWDZIWY
}
else if(warunek_logiczny2){
//instrukcje wykonane jeżeli warunek2 jest PRAWDZIWY
}
else{
//instrukcje wykonane jeżeli warunek1 i warunek 2 są FAŁSZYWE
}
//instrukcje wykonane jeżeli warunek1 jest PRAWDZIWY
}
else if(warunek_logiczny2){
//instrukcje wykonane jeżeli warunek2 jest PRAWDZIWY
}
else{
//instrukcje wykonane jeżeli warunek1 i warunek 2 są FAŁSZYWE
}
Operator trójargumentowy ? :
Jeżeli chcemy, aby zmienna przyjęła jakąś wartość w zależności od warunku logicznego możemy, zamiast bloku if else, użyć specjalnego operatora trójargumentowego:
zmienna = warunek ? wartosc_jak_prawda : wartosc_jak_falsz;
Jest to szybsza i czytelniejsza forma.
Blok switch
Jeżeli chcemy, aby jakoś kod został wykonany w momencie gdy zmienna znajduje się w określonym stanie to możemy użyć bloku switch:
switch ( key ) {
case value1:
// instrukcje dla key równego value1
break;
case value2:
// instrukcje dla key równego value2
break;
default:
break;
}
case value1:
// instrukcje dla key równego value1
break;
case value2:
// instrukcje dla key równego value2
break;
default:
break;
}
W języku Java klucz (key) może być tylko typu int lub char (ten jest odczytywana jako liczba z tablicy unicode), a od wersji 1.5 też enum (coś podobnego do znanych z C/C++ struktur). Warto zauważyć iż słowo kluczowe break jest obowiązkowe. Jeżeli nie użyjemy go to instrukcje będą dalej przetwarzane. Zatem rezultatem takiego kodu:
int i = 0;
switch ( i ) {
case 0:
System.out.println(0);
case 1:
System.out.println(1);
break;
default:
System.out.println("default");
break;
}
switch ( i ) {
case 0:
System.out.println(0);
case 1:
System.out.println(1);
break;
default:
System.out.println("default");
break;
}
będzie:
0
1
1
Pętle
Jeżeli chcemy wykonać jakiś fragment kodu wielokrotnie to możemy wypisać go explicite:
Takie rozwiązanie jest jednak złe. Co jeżeli chcemy wypisać np. wszystkie posty z forum? Jest tego trochę. W dodatku liczba ta wciąż rośnie więc w momencie uruchomienia kodu na pewno nie będzie tam ostatnich postów. Rozwiązaniem tego problemu jest specjalna instrukcja języka - Pętla. Ogólna zasada działania pętli jest bardzo prosta i można ją ująć na trzy sposoby:
WYKONAJ POLECENIE N-KROTNIE
lub
WYKONAJ POLECENIE DOPÓKI SPEŁNIONY JEST WARUNEK
lub
WYKONAJ POLECENIE DLA KAŻDEGO ELEMENTU ZBIORU B
Tak oto zdefiniowaliśmy dwa podstawowe rodzaje pętli w Javie, a ogólniej w programowaniu.
Pętla for
Jest to pętla policzalna, czyli taka o której możemy powiedzieć iż wykona się określoną liczbę razy. Ogólna składnia pętli for wygląda w następujący sposób:
for(int i = 0; warunek; krok){
//instrukcja
}
//instrukcja
}
Uwagi:
- Zmienną i nazywamy Indeksem Pętli
- Indeks może być dowolnym typem prostym poza boolean. Typ char ograniczony jest do 65535
- Warunek może być dowolnym zdaniem logicznym, należy jednak zwrócić uwagę by nie była to tautologia. Otrzymamy wtedy pętle nieskończoną
- Krok pętli może być dowolny jednak tak samo jak w przypadku warunku trzeba uważać na zapętlenie się programu.
Gdzie należy używać pętli for? Odpowiedź na to pytanie jest jedną z kwestii spornych i wywołuje gorące dyskusje. Pętla ta najlepiej sprawdza się gdy chcemy wykonać jakąś operację na wszystkich elementach tablicy. Jest naturalną i najbardziej czytelną dla tego typu problemów:
Odmianą pętli for wprowadzoną w wersji 1.5 jest wersja przyjmująca dwa argumenty. Iterator i warunek. Operuje on na kolekcjach. Przykład:
Collection<Object> col = new HashSet<Object>();
col.add("a");
col.add("b");
for(Iterator<Object> it = col.iterator(); it.hasNext();){
System.out.println(it.next());
}
col.add("a");
col.add("b");
for(Iterator<Object> it = col.iterator(); it.hasNext();){
System.out.println(it.next());
}
Pętle while i do while
Pętle while i do while są pętlami niepoliczalnymi, czyli takimi o których nie możemy powiedzieć ile razy się wykonają. Ich składnia jest następująca:
while(warunekLogiczny){
//instrukcja
}
/*---------*/
do{
//instrukcja
}while(warunekLogiczny)
//instrukcja
}
/*---------*/
do{
//instrukcja
}while(warunekLogiczny)
Uwagi:
- warunek musi być zmienną boolean lub obiektem klasy java.lang.Boolean.
Obie te konstrukcje są bardzo podobne do siebie. Główna różnica polega na tym iż w pętli while warunek jest sprawdzany przed wykonaniem instrukcji, a w pętli do while po wykonaniu instrukcji. Oznacza to, że pętla do while wykona się co najmniej jeden raz. Poniższy przykład ilustruje różnicę:
int i = 0;
while (i < 1) {
System.out.println("while " + i);
i++;
}
do {
System.out.println("do while " + i);
i++;
} while (i < 1);
while (i < 1) {
System.out.println("while " + i);
i++;
}
do {
System.out.println("do while " + i);
i++;
} while (i < 1);
Druga pętla wykona się pomimo iż warunek, 1 < 1, nie jest prawdziwy.
Kiedy używać? Najczęściej pętla while jest wykorzystywana do nasłuchiwania. Polega to na stworzeniu nieskończonej pętli, najczęściej podając jako argument słowo true, której zadaniem jest wykonywanie danego kodu nasłuchującego. Prosty szablon animacji:
while(animuj){
//kod animacji
}
//kod animacji
}
Dopóki flaga animuj jest prawdziwa wykonywana jest animacja. Podobnie ma się sprawa z prostymi serwerami które nasłuchują w ten sposób na portach.
Pętla for element : Iterable
W raz nadejściem Javy w wersji 1.5.0 pojawiła się możliwość użycia konstrukcji for E : I. Ta zdawać by się mogło dziwaczna konstrukcja jest w rzeczywistości odpowiednikiem pętli foreach. Jako argument przyjmuje tablicę lub obiekt klasy implementującej interfejs Iterable. Przykład:
List<Object> l = new LinkedList<Object>();
l.add("a");
l.add("b");
for(Object e : l){
System.out.println(e.toString());
}
l.add("a");
l.add("b");
for(Object e : l){
System.out.println(e.toString());
}
Gdzie stosować? Konstrukcja ta jest najodpowiedniejsza dla wszelkiego rodzaju list, kolekcji i wektorów.
Przerywanie i przechodzenie do następnego kroku w pętlach
Czasami zdarz się że chcemy przerwać lub pominąć krok pętli jeżeli spełniony jest jakiś warunek. Aby uzyskać taki efekt musimy użyć jednego z dwóch słów kluczowych.
break
Jeżeli chcemy przerwać wykonanie pętli jeżeli spełniony jest jakiś warunek to musimy użyć słowa break:
continue
Jeżeli chcemy pominąć jakiś krok w pętli to musimy użyć słowa continue:
Podsumowanie pętli
- Mamy trzy rodzaje pętli.
- Różnią się one zasadą działania.
- Należy uważać by nie popełnić błędu i nie stworzyć pętli nieskończonej
Autor: 4programmers.net
Licencja: Creative Commons