instrukcja_jpwp_cw_2.doc

(233 KB) Pobierz
Wymagania dotyczące uczestników ćwiczenia:

Politechnika Gdańska

Wydział Elektroniki, Telekomunikacji i Informatyki

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Języki programowania wysokiego poziomu (JPWP)

 

 

 

Ćwiczenie 2:

Zastosowania języka programowania Java

 

 

 

 

 

Opracowanie:

dr inż. Jacek Rumiński

Katedra Inżynierii Biomedycznej

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Gdańsk, 2004

 


Opis ćwiczenia laboratoryjnego – JPWP

 

 

 

1. WPROWADZENIE. CEL LABORATORIUM.

 

 

Wymagania dotyczące uczestników ćwiczenia:

-          znajomość podstaw programowania strukturalnego (C),

-          znajomość podstaw programowania obiektowego (C++),

-          znajomość podstaw programowania w języku JAVA (pierwsze dwa wykłady);

-          umiejętność twórczego myślenia.

 

Stosowane technologie i narzędzia w ćwiczeniu:

-          Java – Sun JDK 1.5,

-          Edytor kodu – JCreator,

-          Konsola systemu operacyjnego (konsola/terminal DOS, WIN).

 

Materiały wprowadzające i pomocnicze:

-          Bruce Eckel, Thinking in Java (edycja polska), Helion 2003,

-          Dokumentacja API – Sun JDK 1.5 (wersja HTML, dostępna w czasie laboratorium),

-          Specyfikacja języka Java – http://java.sun.com,

-          Materiały i wiedza z wykładów;

-          Jacek Rumiński, Programowanie uniwersalne, Jacek Rumiński/ACTEN, Gdańsk 2004.

 

Cel laboratorium:

 

Celem ćwiczenia jest praktyczne zapoznanie studentów z podstawą modelowania obiektowego w Javie oraz zasadami korzystania z interfejsów programowych dla określonych zastosowań. W sposób szczególny studenci zapoznają się praktycznie z:

-          cechami obiektowymi języka Java,

-          obsługą we/wy,

-          obsługą grafiki,

-          obsługą sieci i baz danych.

 

 

 

 

 

2. PRZEBIEG LABORATORIUM

 

Ramowy program ćwiczenia (łączny czas pracy 2h15min – 3 godziny lekcyjne):

I. Wprowadzenie kierownika ćwiczenia. Cechy obiektowe języka JAVA – interpretacja przykładów, uzupełnianie brakującego kodu, kompilacja i uruchamianie (40 min.).

II. Obsługa grafiki w Javie – aplikacje i aplety interfejsów graficznych i GUI. interpretacja przykładów, uzupełnianie brakującego kodu, kompilacja i uruchamianie (50 min.).

III. Operacje na strumieniach - interpretacja przykładów, uzupełnianie brakującego kodu, kompilacja i uruchamianie (45 min.).

 

 

UWAGA!

Instrukcja zawiera bogaty materiał dydaktyczny. Ma on pomóc zrozumieć realizowane zadania. Wymagane jest jednak wcześniejsze zapoznanie się studentów z instrukcją, w celu płynnej realizacji zadań.

 

 

I.               Wprowadzenie kierownika ćwiczenia. Cechy obiektowe języka JAVA – interpretacja przykładów, uzupełnianie brakującego kodu, kompilacja i uruchamianie (40 min.).

 

 

I.1. Obiekty

 

Obiekt jest jednostkowym (konkretnym) wystąpieniem (realizacją) Klasy go opisującej (modelującej). Oznacza to, że za pomocą programowania obiektowego tworzony jest model bytu, a nie jego opis ilościowy, tak jak to jest wykonywane w programowaniu proceduralnym/strukturalnym. Opis obiektu w klasie odbywa się poprzez modelowanie jego zachowania (metody) i stanu (pola). Jak wspomniano już na początku tego opracowania może istnieć wiele obiektów danej klasy. Każdy z nich jest jednak jednostkowy i istnieje w pamięci komputera. To co wyróżnia obiekt (czyni go unikalnym) to jego czasoprzestrzeń życia – pamięć. Mogą istnieć obiekty o identycznym opisie, które nie są tym samym obiektem. Poprzez analogię do otaczającego nas świata: mogą istnieć człowiek i jego klon (zatem mają ten sam opis genetyczny), lecz różni ich lokalizacja w czasoprzestrzeni (w świecie). Należy zatem pamiętać, że równość cech nie oznacza równości obiektów. Dostęp do obiektu jest możliwy za pomocą „uchwytu” (odwołanie do pamięci - stery - gdzie przechowywany jest obiekt). Nowy obiekt danej klasy tworzony jest za pomocą instrukcji new z podaniem nazwy klasy, np.:

 

Rycerz luke = new Rycerz();

 

oznacza, że tworzony jest nowy obiekt typu Rycerz, do którego przywiązany jest „uchwyt” luke. Tworzenie obiektu danej klasy bardzo dobrze ilustruje stosowany już w tej pracy kod obsługujący ładowanie klas:

 

Class c = Class.forName(„Rycerz”);

Rycerz luke = c.newInstance();

 

Kod ten wskazuje,  że dla potrzeb tworzenia obiektów klasy Rycerz pobierany jest kod tej klasy, a następnie tworzone jest nowe wystąpienie tej klasy. Oczywiście znacznie prostsze jest stosowanie instrukcji new. Instrukcja „new” wymaga jednak znajomości nazwy klasy w czasie pisania programu.

Jeżeli temu samemu „uchwytowi” przypisany zostanie inny obiekt, wówczas obiekt, na który pierwotnie wskazywał „uchwyt” ginie. Nie ma więc potrzeby w Javie usuwania nieużywanych obiektów, gdyż jest to wykonywane automatycznie. Każdy obiekt jest więc jednostkowym wystąpieniem klasy i ma charakter dynamiczny. Można jednak wyróżnić elementy niezmienne dla obiektów tej samej klasy. Przykładowo liczba Rycerzy nie jest własnością bytu (obiektu) lecz klasy, która opisuje byty tego typu. Oznacza to, konieczność definicji nie dynamicznych lecz statycznych (niezmiennych) pól i metod klasy, do których odwołanie odbywa się nie przez obiekty lecz przez nazwę klasy obiektów. Wszystkie pola i metody statyczne muszą być wyróżnialne poprzez oznacznik static. Określenie pola klasy jako static oznacza, że jego stan jest jednostkowy dla wszystkich obiektów danej klasy - jest własnością klasy, a nie obiektów. Obiekt zmieniając stan pola statycznego zmienia go dla wszystkich innych obiektów. Przykładowo:

 

Skopiować, nagrać (nazwa pliku musi być identyczna jak nazwa klasy publicznej, Republika.java) i skompilować kod z przykładu 2.1.

 

 

Przykład 2.1.

 

//Republika.java

class Rycerz{

              static int liczbaRycerzy=0;

              int numerRycerza =0;

              Rycerz (String imie){

System.out.println(„Rada Jedi głosi: „+ imie+” jest nowym Rycerzem Jedi”);

}

}

public class Republika{

  public static void main (String args[]){

              Rycerz yoda = new Rycerz(„Yoda”);

              Rycerz anakin = new Rycerz(„Anakin”);

Rycerz luke = new Rycerz(„Luke”);

 

              yoda.numerRycerza=1;

              yoda.liczbaRycerzy++;

              System.out.println(„Liczba rycerzy wg. Yody: „+yoda.liczbaRycerzy);

              System.out.println(„Liczba rycerzy wg. Anakina: „+anakin.liczbaRycerzy);

              System.out.println(„Liczba rycerzy wg. Luke’a: „+luke.liczbaRycerzy);

             

              anakin.numerRycerza=2;

              anakin.liczbaRycerzy++;

              System.out.println(„Liczba rycerzy wg. Yody: „+yoda.liczbaRycerzy);

              System.out.println(„Liczba rycerzy wg. Anakina: „+anakin.liczbaRycerzy);

              System.out.println(„Liczba rycerzy wg. Luke’a: „+luke.liczbaRycerzy);

 

              luke.numerRycerza=3;

              luke.liczbaRycerzy++;

              System.out.println(„Liczba rycerzy wg. Yody: „+yoda.liczbaRycerzy);

              System.out.println(„Liczba rycerzy wg. Anakina: „+anakin.liczbaRycerzy);

              System.out.println(„Liczba rycerzy wg. Luke’a: „+luke.liczbaRycerzy);

   }             

}// koniec public class Republika

 

Powyższy przykład ukazuje brak zależności zmiennej liczbaRycerzy od poszczególnych obiektów. Statyczne pola klas są więc doskonałymi elementami przechowującymi informację o stanie klasy (np. ilość otwartych okien, plików, itp.). Poprawna forma wywoływania wartości zmiennych klasy (pól klasy) powinna odwoływać się do nazwy klasy a nie do obiektu, np. Rycerz.liczbaRycerzy zamiast luke.liczbaRycerzy. Tak zapisany kod jest czytelne, a inny programisty czytający taki kod doskonale zrozumie jego znaczenie bez potrzeby analizy definicji klasy Rycerz.

Możliwe jest również stworzenie statycznej metody, której działanie jest wywoływane bez konieczności tworzenia obiektu klasy, w ciele której zdefiniowano statyczną metodę. Przykładowa metoda statyczna może zwracać liczbę rycerzy (z przykładu 3.1) przechowywaną jako pole statyczne poprzez:

 

public static int liczbaR(){

              int lR=Rycerz.liczbaRycerzy;

              System.out.println(„Liczba Rycerzy Jedi w Republice wynosi: „+lR);

              return lR;

}

Należy pamiętać, co zostało już przedstawione wcześniej, tworzenie obiektu powoduje wywołanie procedury jego inicjowania zwanej konstruktorem. Konstruktor jest metodą o tej samej nazwie co nazwa klasy, dla której tworzony jest obiekt. Konstruktor jest wywoływany automatycznie przy tworzeniu obiektu. Stosuje się go do podawania argumentów obiektowi, oraz do potrzebnej z punktu widzenia danej klasy grupy operacji startowych. Wywołanie konstruktora powoduje zwrócenie referencji do obiektu danej klasy. Nie można więc deklarować konstruktora z typem void. Kod konstruktora może zawierać wywołanie innego konstruktora tej samej klasy lub wywołanie konstruktora nadklasy. Kolejność wołania konstruktorów w kodzie danego konstruktora jest następująca:

 

NazwaKlasy(argumety){

              this(argumenty1); //wywołanie innego konstruktora tej samej klasy

              super(argumenty1); //wywołanie konstruktora nadklasy

              kod;

}

Jeżeli programista jawnie nie zdefiniował konstruktora to jest on tworzony domyślnie, jako kod pusty bez argumentów, np.:

 

NazwaKlasy(){

}

Rozważania dotyczące klas zamieszczono na początku tego opracowania. W Javie istnieje możliwość sprawdzenia czy dany obiekt jest wystąpieniem danej klasy. Do tego celu służy operator instanceof, np. luke instanceof Rycerz. Efektem działania operatora jest wartość logiczna true - jeśli obiekt jest danej klasy, lub false - w przeciwnym przypadku.

 

 

I.2. Dziedziczenie i abstrakcje

 

Nazwa dziedziczenie związana jest z procesem ewolucyjnym, w którym potomkowie posiadają pewne cechy rodziców. Podobne znaczenie tej nazwy jest używane w programowaniu obiektowym. Otóż zakładając, że każdy typ lub gatunek może mieć swój podgatunek, a więc zawężony i uszczegółowiony opis obiektów, to właściwości tych obiektów będą wynikały zarówno ze specyfikacji gatunku jak i podgatunku. Inaczej mówiąc, elementy klas nadrzędnych będą również elementami ich pochodnych (klas dziedziczących po klasie nadrzędnej). Przykładowo klasą nadrzędną może być klasa Rycerz, klasami po niej dziedziczącymi mogą być klasy Człowiek, Gungan, Kobieta, itp. W Javie określenie dziedziczenia odbywa się poprzez użycie słowa kluczowego extends (rozszerza). Przykładowa deklaracja:

 

class Kobieta extends Rycerz{

(...)

}

 

określa, że tworzona klasa Kobieta dziedziczy po klasie Rycerz. W Javie możliwe jest tylko dziedziczenie typu jeden-do-jednego, co oznacza, że klasa może dziedziczyć tylko po jednej klasie nadrzędnej. Ponieważ klasa nadrzędna może również dziedziczyć po jednej klasie dla niej nadrzędnej otrzymuje się specyficzne drzewo dziedziczenia w Javie. Nadrzędną klasą dla wszystkich klas w Javie jest klasa Object. Wszystkie klasy bezpośrednio lub pośrednio z niej dziedziczą, czyli klasa ta stanowi korzeń drzewa dziedziczenia. Ponieważ znajduje się ona w pakiecie java.lang.*, można powiedzieć, że wszystkie klasy będą korzystały z tego pakietu. W Javie możliwe jest więc dziedziczenie wielopoziomowe polegające na tym, że wiele klas może dziedziczyć z jednej (lecz nie odwrotnie).

 

Kolejnym bardzo ważnym elementem programowania obiektowego jest odwołanie się do rozważań w filozofii nad możliwością istnienia bytów ogólnych (pojęć ogólnych, uniwersalii, itp.). Przykładowe pytanie tych rozważań może być następujące: Czy może istnieć obiekt ogólny Rycerz? Teoretycznie w Javie nie może istnieć taki obiekt. Zatem pojęcie ogólne (bez konkretnej realizacji) jest abstrakcją. Ponieważ nie można stworzyć obiektów takiej klasy, klasa jest oznaczana jako abstrakcyjna - abstract. Klasa abstrakcyjna zawiera w opisie obiektu również metody abstrakcyjne (co najmniej jedną; metoda abstrakcyjna – metoda bez definicji). W celu stworzenia obiektu, dla którego opis  znajduje się w klasie abstrakcyjnej  należy stworzyć klasę pochodną klasy abstrakcyjnej i podać rzeczywisty opis (realizację) wszystkim metodom abstrakcyjnym zdefiniowanym w klasie abstrakcyjnej. Klasy abstrakcyjne stosuje się wówczas, gdy na danym stopniu opisu ogólnego możliwych obiektów nie można podać rzeczywistej realizacji jednej lub wielu metod (modelowanie, projektowanie). Przykładowo tworząc klasę Statek nie można podać metody obliczającej pole powierzchni statku, gdyż ta zależy od danego typu statku kosmicznego, dla którego dany jest wzór na pole powierzchni. Tak więc klasa pochodna typu statku np.: GwiezdnyNiszczyciel, może zdefiniować w swym ciele metodę o tej samej nazwie co w klasie Statek, lecz określonej implementacji (realizacji). Dzięki temu wszystkie typy statków opisywane własnymi klasami będą miały podobny opis ogólny, ułatwiający wymianę informacji. Problem ten zilustrowano przykładem 2.2:

 

Skopiować, nagrać (nazwa pliku musi być identyczna jak nazwa klasy publicznej, Flota.java) i skompilować kod z przykładu 2.2.

 

Przykład 2.2.

 

//Flota.java

 

abstract class Statek{

              int numerStatku;

              int liczbaDzial;

              long predkoscMax;

              public abstract int polePowierzchni();

              public void informacje(){

                            System.out.println("Liczba dział = "+liczbaDzial);

                            System.out.println("Prędkość maksymalna = "+predkoscMax);

                            System.out.println("Numer identyfikacyjny = "+numerStatku);

}

 

} // koniec abstract class Statek{

 

class GwiezdnyNiszczyciel extends Statek{

              int wysTrojkata;

              int dlgPodstawy;

              GwiezdnyNiszczyciel(int numer){

              numerStatku=numer;

}

//IMPLEMENTACJA METODY ABSTRAKCYJNEJ z klasy Statek

              public int polePowierzchni(){

                            return (wysTrojkata*dlgPodstawy/2);

}

}// koniec class GwiezdnyNiszczyciel

 

class GwiezdnySokol extends Statek{

              int szer;

              int dlg;

              GwiezdnySokol(int numer){

              numerStatku=numer;

}

//IMPLEMENTACJA METODY ABSTRAKCYJNEJ z klasy Statek

              public int polePowierzchni(){

                            return (dlg*szer);

}

} // koniec class GwiezdnySokol

 

public class Flota{

              public static void main(String args[]){

                            GwiezdnyNiszczyciel gw1 = new GwiezdnyNiszczyciel(1);

                            gw1.wysTrojkata=200;

                            gw1.dlgPodstawy=500;

              ...

Zgłoś jeśli naruszono regulamin