background image

 

Jan Bielecki 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Java 

od  

podstaw

 

background image

 

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

 

Moim przyjaciołom 

 

Hanne i Rainerowi 

 

Langmaack

 

background image

 

Spis treści 

 

Od Autora 

 

Część I 

Język Java 

 

Aplikacje i aplety 
Modyfikowanie apletu 
Obsługiwanie zdarze
ń 
Wykonywanie w
ątków 
Obsługiwanie wyj
ątków 

 

Część II  Środowisko rozwojowe 

 

Programowanie manualne 
Programowanie wizualne 

 

Część III  Podstawy programowania 

 

Leksemy 
Zmienne 
Instrukcje  
Tablice 
Obiekty 
Strumienie 

 

Część IV  Programowanie obiektowe 

 

Hermetyzacja 
Dziedziczenie 
Interfejsy 
Konwersje 
Polimorfizm 
Kolekcje 

 

Część V  Programowanie zdarzeniowe 

 

Klasa zdarzeniowa 
Obsługiwanie zdarze
ń 
Odtwarzanie pulpitu 

 

Część VI  Programowanie współbieżne 

 

Stany wątków 

background image

 

Priorytety 
W
ątek główny 
Tworzenie w
ątków 
Synchronizowanie w
ątków 
Instrukcja synchronizuj
ąca 
Procedury synchronizowane 
Monitor 
Impas 

 

Część VII  Programowanie apletów 

 

Opis 
Wykonanie 
Otoczenie 
Grafika  
Klawiatura 
Myszka 
Kursory 
Obrazy 
Rozkłady 
Sterowniki 
Pojemniki 
Kontrolki 
D
źwięki 
Animacje 
Przesyłanie 
Komunikacja 
Przeł
ączenia 

 
Literatura 

 

Słownik terminów 

 

Dodatki 

 

A  Priorytety operatorów 
B  Parametry zdarze
ń 
C  Klasa uruchomieniowa 
D  Styl programowania 
E  Typy predefiniowane 
F  Operacje i operatory 

 

 
 
 
 

background image

 

 

background image

 

Od Autora 

 
Nie  ulega  wątpliwości,  że  to  co  się  dzieje  wokół  Javy  jest  rewolucją.  Po  długim  okresie  przestoju,  w  którym 
aktywność  w  zakresie  języków  programowania  ograniczała  się  do  porządkowania  tego,  co  było  już  znane  od 
kilkunastu lat, pojawił się nowy język, który niemal z dnia na dzień zyskał aprobatę zarówno specjalistów, jak i 
zwykłych użytkowników Internetu
 
Swój  sukces  zawdzięcza  Java  temu,  iż  wyszła  spod  ręki  zawodowego  programisty,  że  wykorzystano  w  niej 
najlepsze  pomysły  zawarte  w  językach C++Objective C  i  Smalltalk oraz że wbudowując w nią mechanizmy 
programowania  strukturalnego,  obiektowego,  zdarzeniowego  i  współbieżnego  przygotowano  grunt  pod 
programowanie rozproszone
 
Dzięki  odważnej  decyzji  implementowania  Javy  jako  języka  interpretowanego,  którego  kod  wynikowy  jest 
wykonywany na Maszynie Wirtualnej, zapewniono przenośność programów między odmiennymi platformami 
sprzętowymi  i  systemowymi,  i  to  nie  tylko  na  poziomie  kodu  źródłowego,  ale  również  na  poziomie  kodu 
binarnego.  
 
Powszechne zaakceptowanie Javy przekroczyło najśmielsze oczekiwania jej twórcy Jamesa Goslinga. W ślad 
za  wsparciem  udzielonym  jej  przez  przez  wiodące  firmy  komputerowe:  Sun,  Microsoft,  IBM,  Apple,  Oracle
CorelNetscapeSymantecBorlandHewlett PackardSilicon GraphicsToshiba, poszły liczne implementacje 
wizualnych  Środowisk  Rozwojowych,  w  tym  Cafe  i  Visual Cafe  (Symantec),  Open JBuilder  (Borland), 
SuperCede  (Asymetrix),  Parts for Java  (ParcPlace),  Visual J++  (Microsoft),  Java WorkShop  (Sun)  i 
CodeWarrior (Metrowerks). 
 
Opracowano  wiele  wyspecjalizowanych  bibliotek  języka,  opublikowano  specyfikację  komponentów 
JavaBeans,  implementowano  konfigurowalną  przeglądarkę  HotJava Views  oraz  wprowadzono  na  rynek 
komputery  sieciowe  JavaStation  wyposażone  w  system  operacyjny  JavaOS.  W  dążeniu  do  pełnego 
upowszechnienia  Javy  podjęto  produkcję  układu  scalonego  PicoJava,  umożliwiającego  wyposażenie  w  nią 
takich miniaturowych urządzeń konsumenckich jak komórkifaksy i kalkulatory
 
 
Niniejszą  książkę,  zapowiedzianą  w  przedmowie  do  Java po C++  napisałem  dla  tych,  którzy  mieli  już 
styczność  z  programowaniem,  ale  nie  znają  C++  ani  nawet  języka  C.  W  tej  postaci  przedkładam  ją 
informatykom  zafascynowanym  nowymi  narzędziami  Internetu  oraz  tym  wszystkim,  którzy  w  ślad  za 
wiodącymi  ośrodkami  akademickimi  porzucą  Turbo  Pascal,  Delphi  i  Visual  Basic,  decydując  się  na 
zainwestowanie swego czasu w poznanie narzędzia następnego wieku. 
 
Lektura  tekstu,  którego  kolejnymi  częściami  są:  Język Java,  Środowisko  rozwojowe,  Podstawy 
programowania
,  Programowanie  obiektowe,  Programowanie  zdarzeniowe,  Programowanie  współbieżne
Programowanie  apletów  i  Słownik terminów  oraz  bardzo  ważne  dla  pełnego  poznania  języka    Dodatki
umożliwia  łagodne  wejście  w  tematykę  programowania  w  Javie,  przygotowując  do  lektury  mojej  następnej 
książki  pt.  Java  -  programowanie  wizualne,  poświęconej  współczesnej  metodyce  programowania,  wstępnie 
przedstawionej tu w rozdziale o środowiskach rozwojowych 
 
Jestem  przekonany,  że  niniejszy  podręcznik  w  pełni  zaspokoi  potrzeby  tych,  którzy  chcą  poznać  Javę  od 
podstaw
  oraz  że  dostarczy  obszernego  materiału  praktycznego  nie  tylko  słuchaczom  moich  wykładów  w 
Politechnice  Warszawskiej  i  Polsko-Japońskiej  Wyższej  Szkole  Technik  Komputerowych,  ale  również  tym 
wszystkim, którzy podzielą pogląd, że gruntowne poznanie Javy jest bardzo dobrą inwestycją. 
  
Jan Bielecki 

background image

 

Część I 

Język Java 

 
Java jest nowym językiem programowania opartym na uznanych językach starszej generacji. We wczesnej fazie 
powstawania była projektowana jako narzędzie do programowania komunikujących się ze sobą konsumenckich 
urządzeń  elektronicznych,  ale  wkrótce  przekształcono  ją  w  narzędzie  do  programowania  aplikacji  w  sieciach 
korporacyjnych i w Internecie
 
Główną  zaletą  Javy  jest  przenośność  programów  na  poziomie  kodu  binarnego.  Dzięki  temu  program 
uruchomiony  w  pewnym  środowisku  operacyjnym  (np.  Windows)  może  być  wykonany  w  dowolnym  innym 
(np. Unix MacOS). Tę unikalną właściwość zawdzięcza Java temu, iż jest językiem interpretowanym, a nie 
kompilowanym. 
 
Program  źródłówy  w  Javie  przygotowuje  się  za  pomocą  edytora,  a  następnie  poddaje  się  kompilacji
uruchomieniu  i  testowaniu.  Produktem  końcowym  jest  B-kod  Maszyny  Wirtualnej  (JavaVM)  umieszczony  w 
pliku z rozszerzeniem .class
 
Warunkiem wykonania B-kodu jest wyposażenie platformy programistycznej w emulator Maszyny Wirtualnej
układ scalony, albo program napisany w rodzimym języku platformy. Inwestycja ta jest niewielka i została już 
wykonana we wszystkich liczących się systemach (WindowsUnixMacOS). 
 
 
_________________________________________________________________________________________ 

Aplikacje i aplety 

 
Program  napisany  w  Javie  jest  aplikacją  lub  apletem.  Aplikacja  jest  programem  samodzielnym,  a  aplet  jest 
programem zanurzonym w aplikacji, na przykład w przeglądarce Netscape
 
Ponieważ  przeglądarka  przydziela  apletowi  wydzielony  obszar  okna,  nazywany  zazwyczaj  pulpitem,  więc 
określenie aplet utożsamia się często nie z samym jego programem, ale z tym co ten program wykreśli. Z tego 
powodu  mają  sens  i  mogą  być  używane  takie  określenia  jak:  napisanie  apletu  (utworzenie  kodu  programu)  i 
wyświetlenie apletu (uwidocznienie wyników wykonania kodu).  
 
 

Struktura programu 

 
Każdy  program  składa  się  z  co  najmniej  jednego  modułu  źródłowego  umieszczonego  w  pliku.  Tekst  modułu 
zapisuje się w układzie swobodnym, z dowolnie dodanymi odstępami (spacjamitabulacjami i zakończeniami 
wierszy
). 
 
Odstępy można umieszczać poza jednostkami leksykalnymi programu (słowami kluczowymiidentyfikatorami
operatorami ogranicznikami).  
 
Szczególną  odmianę  odstępu  stanowi  komentarz  wierszowy.  Zaczyna  się  on  od  pary  znaków  //  (skośnik
skośnik), a kończy w miejscu zakończenia wiersza. Cały taki napis jest traktowany jak jedna spacja, a więc jest 
ignorowany
 
 

Aplikacje 

 
Aplikacją  jest  program  składający  się  z  zestawu  definicji  klas,  z  których  przynajmniej  jedna  jest  publiczna  
zawiera publiczną i statyczną funkcję główną. 
 
Nagłówek funkcji głównej ma postać 
 

 

 

 

public static void main(String args[]) 

 
Wykonanie aplikacji kończy się w chwili, gdy w funkcji głównej wykona się instrukcję powrotu 

background image

 

 

 

return; 

 
albo gdy z dowolnej procedury (w tym z funkcji głównej) wywoła się funkcję exit
 
Uwaga:  Przytoczona  instrukcja  jest  domniemywana  przed  każdą  klamrą  kończącą  definicję  procedury  o 
rezultacie void, a więc jawne jej użycie jest na ogół zbyteczne. 
 
Przykład Najprostsza aplikacja 
 

public  
class Master { 
    public statatic void main(String args[]) 
    { 
    } 

 
Klasa Master jest publiczna. Zawiera ona publiczną i statyczną funkcję główną. 
 
Tuż przed klamrą kończącą definicję funkcji domniemano instrukcję powrotu. 
 
 

Aplikacje znakowe 

 
Wyniki  aplikacji  znakowej  są  wyprowadzane  na  konsolę  (ekran  monitora),  a  w  środowisku  graficznym  (np. 
Windows 95) do okna konsoli
 
Następująca  aplikacja,  pokazana  podczas  wykonania  na  ekranie  Aplikacja  znakowa,  wyprowadza  do  okna 
konsoli napis  
 

 

Hello, I am JanB. 

 
Ekran Aplikacja znakowa 

### textapp.gif 

 
Przykład Aplikacja znakowa 
 

public 
class Master { 
    public static void main(String args[]) 
    { 
               // wyprowadzenie tekstu na konsol

ę

 

        System.out.println("Hello, I am JanB."); 
    } 

 

Dzięki  użyciu procedury println,  a nie  print, zapewniono wyprowadzenie wiersza znaków, a nie tylko znaków 
zawartych w cudzysłowach. 
 
 

Wstrzymanie zakończenia aplikacji 

Jeśli  aplikacja  znakowa  jest  wykonywana  w  środowisku  graficznym,  to  w  chwili  jej  zakończenia  usuwa  się  z 
ekranu  okno  konsoli.    W  celu  uniknięcia  towarzyszącego  temu  "zniknięcia"  wyników,  zaleca  się  użycie 
instrukcji 
 

 

System.in.read(); 

 
Spowoduje  to  wstrzymanie  zamknięcia  okna  konsoli  do  chwili  wprowadzenia  z  klawiatury  dowolnego  znaku 
(np. Enter). 
 
W takim jednak wypadku należy nagłówek procedury zawierającej tę instrukcję uzupełnić frazą  
 

background image

 

 

throws IOException 

 
a aplikację poprzedzić poleceniem 
 

 

import java.io.IOException 

albo  

 

import java.io.*; 

 
Na przykład 
 

import java.io.IOException; 
 
public 
class Master { 
    public static void main(String args[]) 
    { 
               // wyprowadzenie tekstu na konsol

ę

 

        System.out.println("Hello, I am JanB."); 
        System.in.read(); 
    } 

 
 

Aplikacje graficzne 

 
Wyniki aplikacji graficznej są wyprowadzane do własnego okna graficznego. Zamknięcie okna nie jest na ogół 
wystarczające i w chwili zamknięcia aplikacji musi być wywołana funkcja exit
 
Do  komunikowania  się  aplikacji  z  oknem  graficznym  jest  wykorzystywany  wykreślacz  dysponujący 
informacjami  o  właściwościach  środowiska  (np.  o  rodzaju  użytej  karty  graficznej).  Dzięki  temu  unika  się 
programowego  rozpoznawania  środowiska  i  umożliwia  pisanie  programów,  które  mogą  być  wykonywane  na 
dowolnej platformie. 
 
Uwaga: Wykreślacz związany z oknem można utworzyć dopiero wówczas, gdy okno jest już wyświetlone na 
ekranie (to jest dopiero po wywołaniu metody show). 
 
Następująca aplikacja, pokazana podczas wykonania na ekranie Aplikacja graficzna, wykreśla w oknie napis 
 

 

Hello, I am JanB. 

 
Ekran Aplikacja graficzna 

### graphapp.gif 

 

import java.io.IOException; 
import java.awt.*; 
 
public  
class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
              // utworzenie okna graficznego 
        Frame frame = new MyFrame("Results"); 
              // okre

ś

lenie rozmiarów okna 

        frame.resize(160, 80); 
              // wy

ś

wietlenie okna 

        frame.show(); 
              // utworzenie wykre

ś

lacza 

              // komunikuj

ą

cego si

ę

 z oknem 

        Graphics gDC = frame.getGraphics(); 
              // wykre

ś

lenie tekstu  

              // za po

ś

rednictwem wykre

ś

lacza 

        gDC.drawString("Hello, I am JanB.", 10, 20); 
        System.out.println("Press Enter to exit!); 
        System.in.read(); 
        System.exit(0); 

background image

 

10 

    } 

 
class MyFrame extends Frame { 
    MyFrame(String caption) 
    { 
              // przekazanie nazwy okna klasie Frame 
        super(caption);  
    } 
              // obsługa zdarzenia WINDOW_DESTROY 
              // generowanego w chwili zamkni

ę

cia okna 

    public boolean handleEvent(Event evt) 
    { 
        if(evt.id == Event.WINDOW_DESTROY) { 
            hide();                 // ukrycie okna 
            dispose();              // zniszczenie okna 
            return true; 
        } else  
            return super.handleEvent(evt); 
    } 

 
Program  aplikacji  składa  się  z  definicji  klas  Master  i  MyFrame.  Klasa  Master  zawiera  definicję  funkcji 
głównej, a klasa MyFrame wywodzi si
ę z klasy bibliotecznej Frame
 
Poza klasą Frame, aplikacja posługuje się klasą biblioteczną Graphics. Kod tych klas jest zawarty w pakiecie 
java.awt, wł
ączonym do programu za pomocą deklaracji importu 
 

import java.awt.*; 

 
W  celu  uniknięcia  przedwczesnego  zamknięcia  aplikacji,  wywołanie  funkcji  exit  poprzedzono  wykonaniem 
instrukcji 
 

System.in.read(); 

 
 

Aplety 

 
Apletem  jest  program  składający  się  z  zestawu  definicji  klas,  z  których  przynajmniej  jedna  jest  publiczna  
wywodzi się (extends) od klasy Applet.  
 
Tak  zdefiniowana  klasa  apletowa  zawiera  w  ogólnym  wypadku  procedury  init,  start,  paint,  stop,  destroy 
stanowiące jej metody. Wymienione metody są wywoływane przez przeglądarkę w podanej kolejności. Metoda 
init oraz metoda destroy jest  wywoływana jednokrotnie. Metody startstop paint mogą być wywoływane 
wielokrotnie
 
Uwaga: Program apletu nie musi zawierać takich typowych wywołań System.in.read() i System.exit(0)
 
W  najprostszym  przypadku  wystarcza  zdefiniowanie  w  aplecie  tylko  metody  paint.  Jest  ona  wywoływana 
wkrótce  po  wykonaniu  metody  init,  a  także  w  każdej  sytuacji  gdy  zaistnieje  potrzeba  odtworzenia  pulpitu 
apletu (na przykład po zasłonięciu i odsłonięciu go przez pewne okno). 
 
Aby  przeglądarka mogła wyświetlić aplet, należy przekazać jej opis apletu. Uproszczony do minimum ma  on 
postać 
 

<applet  
    code   = Nazwa.class  
    width  = Szeroko

ść

  

    height = Wysoko

ść

 

>  
</applet> 

 
w  której  Nazwa  jest  nazwą  klasy  apletowej,  a  Szerokość  i  Wysokość  określają  poziomy  i  pionowy  rozmiar 
apletu. 

background image

 

11 

 
Następujący aplet, pokazany podczas wykonania na ekranie Pozdrowienie, wykreśla na swoim pulpicie napis 
 

 

Hello, I am JanB. 

 
Ekran Pozdrowienie 

### hello.gif 

 

<applet code=Master.class width=200 height=80>  
</applet> 
============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    Font font;                                   // czcionka 
    public void init() 
    { 
        font = new Font("TimesRoman",            // krój 
                        Font.BOLD | Font.ITALIC, // styl 
                        24);                     // rozmiar 
    } 
    public void paint(Graphics gDC) 
    { 
        gDC.setFont(font);          // wstaw do wykre

ś

lacza 

        gDC.drawString("Hello, I am JanB.", 10, 50); 
    } 

 
Program apletu składa się z definicji klasy Master wywodzącej się od klasy Applet. 
 
Klasa Master zawiera definicje metod init paint. Metoda init jest wywoływana jednokrotnie, a metoda paint 
jest wywoływana co najmniej jeden raz. 
 
W  metodzie  init  przygotowano  czcionkę  do  wykreślenia  tekstu.  W  metodzie  paint  jest  ona  wstawiana  do 
wykre
ślacza, dzięki czemu zapowiedziany napis jest wykreślany czcionką TimesRoman, pogrubioną (BOLD) i 
pochylon
ą (ITALIC), o rozmiarze 24 punktów (1 punkt drukarski to około 1/72 cala). 
 
 

Aplikacje a aplety 

 
Podział na aplikacje i aplety jest umowny. Następujący program jest zarówno apletem jak i aplikacją. 
 

<applet code=Master.class width=100 height=100>  
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
import java.io.IOException; 
 
public 
class Master extends Applet { 
    public static void main(String args[]) 
    { 
        System.out.println("I am an application."); 
        System.in.read(); 
        System.exit(0); 
    } 
    public void paint(Graphics gDC) 
    { 
         gDC.drawString("I am an applet.", 10, 10); 
    } 

 

background image

 

12 

W zależności od sposobu potraktowania, program wyprowadza napis 
 

 

I am an applet. 

albo 

 

I am an application. 

 
 
_________________________________________________________________________________________ 

Modyfikowanie apletu 

 
Każda  klasa  definiująca  aplet  jest  opisem  rodziny  obiektów.  Jeśli  podklasa  (klasa  pochodna)  wywodzi  się  od 
innej klasy, to jej obiekty składają się ze składników jej nadklasy (klasy bazowej) oraz dodatkowo z własnych 
składników podklasy. 
 
Jeśli  w podklasie występuje  metoda o identycznej  sygnaturze jak pewna  metoda  nadklasy, to przedefiniowuje 
ona metodę nadklasy. Dzięki temu, definiując podklasę wystarczy podać w niej tylko metody przedefiniowujące 
oraz metody dodatkowe. 
 
Uwaga:  Metoda  przedefiniowująca  nie  może  być  bardziej-prywatna  niż  metoda  przedefiniowywana.  W 
szczególności,  jeśli  metoda  przedefiniowywana  jest  publiczna,  to  metoda  przedefiniowująca  także  musi  być 
publiczna. 
 
Następujący  aplet,  pokazany  podczas  wykonania  na  ekranie  Modyfikowanie,  ilustruje  zasadę  modyfikowania 
apletu przez zastosowanie przedefiniowania metod. 
 
Ekran Modyfikowanie 

### modify.gif 
 
<applet code=Master.class width=200 height=80>  
</applet> 
============================================== 

 

// Plik Master.java 
 
import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    Font font;                                   // czcionka 
    public void init() 
    { 
        font = new Font("TimesRoman",            // krój 
                        Font.BOLD | Font.ITALIC, // styl 
                        24);                     // rozmiar 
    } 
    public void paint(Graphics gDC) 
    { 
        gDC.setFont(font);          // wstaw do wykre

ś

lacza 

        gDC.drawString("Hello, I am JanB.", 10, 50); 
    } 

 
// Plik Master2.java 
 
import java.applet.*; 
import java.awt.*; 
 
public 
class Master2 extends Master { 
    public void init() 
    { 
        font = new Font("Helvetica",   // krój 
                        Font.BOLD,     // styl 
                        18);           // rozmiar 
    } 

background image

 

13 

 
Klasa Master2 wywodzi się od klasy Master, a więc pośrednio od klasy Applet
 
Klasa Master2 zawiera wszystkie składniki apletu Master (w tym jego metod
ę paint), ale przedefiniowuje jego 
metod
ę init
 
Dlatego wykonanie apletu Master2 powoduje wykreślenie napisu 
 

 

Hello, I am JanB. 

 
18 pt czcionką Helvetica, a nie 24 pt czcionką TimesRoman
 
 
_________________________________________________________________________________________ 

Obsługiwanie zdarzeń 

 
Od  czasu  pojawienia  się  Javy,  można  zauważyć  powszechne  wyposażanie  stron  WWW  w  interakcyjne  aplety, 
reagujące  na  takie  zdarzenia  zewnętrzne  jak  kliknięcie  przyciskiem  myszki  albo  naciśnięcie  klawisza 
klawiatury. 
 
Obsługiwanie  zdarzeń  zewnętrznych  odbywa  się  za  pomocą  predefiniowanych  metod  klas  komponentowych 
(m.in. PanelApplet), opisujących takie obiekty oblicza graficznego (Graphical interface) jak etykiety (Label), 
przyciski (Button), ramki (Frame), itp. 
 
Obsługa  domniemana  polega  zazwyczaj  na  zignorowaniu  zdarzenia,  dlatego  w  celu  dostarczenia  własnej 
obsługi zdarzenia zewnętrznego, należy dokonać przedefiniowania metody domniemanej. 
 
W  szczególności,  ponieważ  w  klasie  komponentowej  Applet  obsługa  przeciągania  kursora  myszki  jest 
określona przez metodę 
 

public boolean mouseDrag(Event evt, int x, int y) 

    return false;           // zignoruj zdarzenie 

 
więc w celu dostarczenia własnej obsługi wystarczy przedefiniować metodę mouseDrag
 
Następujący  aplet,  pokazany  podczas  wykonywania  na  ekranie  Wykreślanie  z  ręki,  ilustruje  zasadę 
obsługiwania zdarzeń. 
 
Ekran Wykreślanie z ręki 

### scribe.gif 

 

<applet code=Master.class width=200 height=80>  
</applet> 
============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    int xPos, yPos; 
    Graphics gDC; 
    public void init() 
    { 
        gDC = getGraphics(); 
    } 
    public boolean mouseDown(Event evt, int x, int y) 
    { 
        xPos = x; 
        yPos = y; 

background image

 

14 

        return true; 
    } 
    public boolean mouseDrag(Event evt, int x, int y) 
    { 
        gDC.drawLine(xPos, yPos, x, y); 
        xPos = x; 
        yPos = y; 
        return true; 
    } 
    public boolean mouseUp(Event evt, int x, int y) 
    { 
        repaint(); 
        return true; 
    } 
    public void paint(Graphics gDC) 
    { 
        gDC.drawString("Drag mouse to draw!", 10, 20); 
    } 

 
Wykonanie metody init powoduje utworzenie wykreślacza związanego z pulpitem apletu. 
 
Wykonanie  metody  paint  powoduje  wykreślenie  zachęty  do  wykreślania  linii  metodą  przeciągania  kursora 
myszki. 
 
Wykonanie metody mouseDown powoduje zapamiętanie współrzędnych punktu naciśnięcia przycisku myszki. 
 
Każdorazowe  wykonanie  metody  mouseDrag  powoduje  wykreślenie  odcinka  łączącego  punkt  poprzedni  z 
bie
żącym. 
 
Wykonanie metody mouseUp powoduje wywołanie metody repaint
 
Wywołanie metody repaint powoduje, 
że po zakończeniu wykonywania metody z której ją wywołano (tu metody 
mouseUp), nast
ąpi wywołanie metody update (domyślnie wyczyszczenie pulpitu i wywołanie metody paint). 
 
 
_________________________________________________________________________________________ 

Wykonywanie wątków 

 
Oprogramowanie ruchomej grafiki wymaga utworzenia co najmniej jednego dodatkowego wątku: niezależnego 
od obsługi zdarzeń, przepływu sterowania przez B-kod programu. 
 
W  celu  ustanowienia  wątku  należy  utworzyć  obiekt  klasy  Thread  oraz  określić  w  jakiej  klasie  znajduje  się 
metoda run, określająca czynności wątku. 
 
Uwaga:  Klasa  zawierająca  metodę  run  musi  implementować  interfejs  Runnable.  Fakt  ten  wyraża  się  za 
pomocą słowa kluczowego implements
 
Następujący  aplet,  pokazany  podczas  wykonywania  na  ekranie  Wykonywanie  wątków,  ilustruje  zasadę 
programowania współbieżnego z wykorzystaniem wątków. 
 
Ekran Wykonywanie wątków 

### thread.gif 

 

<applet code=Master.class width=100 height=100>  
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    Thread thread; 

background image

 

15 

    Graphics gDC; 
    Color backGround; 
    Circle circle; 
    public void init() 
    { 
        gDC = getGraphics(); 
        backGround = getBackground(); 
        circle = new Circle(gDC, backGround); 
        thread = new Thread(circle); 
        thread.start(); 
    } 

 
class Circle implements Runnable{ 
    Graphics gDC; 
    Color backGround; 
    Circle(Graphics gdc, Color back) 
    { 
        gDC = gdc; 
        backGround = back; 
    } 
    public void run() 
    { 
        for(int i = 0; i < 100 ; i++) { 
            gDC.setColor(Color.black); 
            gDC.drawOval(i, i, i/2, i/2); 
            try { 
                Thread.currentThread().sleep(100); 
            } 
            catch(InterruptedException e) { 
            }    
            gDC.setColor(backGround); 
            gDC.drawOval(i, i, i/2, i/2); 
        }     
    } 

 
Wykonanie instrukcji 
 

circle = new Circle(gDC, backGround); 

 
powoduje utworzenie obiektu klasy Circle, którego składnikiem jest metoda run
 
Wykonanie instrukcji  
 

thread = new Thread(circle); 

 
powoduje utworzenie obiektu zarządzającego wątkiem. 
 
Wykonanie instrukcji 
 

thread.start(); 

 
powoduje  utworzenie  wątku  realizowanego  przez  metodę  run  (tu:  wykreślenie  okręgu  poruszającego  się  po 
przek
ątnej apletu). 
 
Zakończenie wykonywania metody run powoduje zniszczenie wątku. 
 
 
_________________________________________________________________________________________ 

Obsługiwanie wyjątków 

 
Podczas  wykonywania  programu  mogą  wystąpić  sytuacje  wyjątkowe:  dzielenie  przez  0,  dostarczenie  danej  o 
złym formacie, albo napotkanie końca pliku. 
 

background image

 

16 

Z  miejsca  wystąpienia  sytuacji  wyjątkowej  jest  wysyłany  wyjątek:  obiekt  szczegółowo  opisujący  sytuację 
wyjątkową.  Dzięki  temu,  że  wyjątek  może  być  przechwycony,  a  sytuacja  wyjątkowa  obsłużona,  stwarza  się 
szansę wyjścia z powstałej sytuacji wyjątkowej i umożliwia kontynuowanie wykonywania programu. 
 
Do  jawnego  wysyłania  wyjątków  służy  instrukcja  throw,  a  do  przechwytywania  i  obsługiwania  wyjątków 
instrukcja  try.  Jeśli  wywołanie  pewnej  procedury  może  spowodować  wysłanie  wyjątku,  to  wymaga  się,  aby 
zawarto  je  w  bloku  instrukcji  try,  albo  aby  w  nagłówku  procedury  wywołującej  umieszczono  frazę  throws 
wyszczególniającą ten wyjątek. 
 
Uwaga: Wyszczególnienie  nazwy wyjątku we  frazie  throws metody przedefiniowującej jest dozwolone  tylko 
wówczas, gdy taka nazwa występuje albo może wystąpić we frazie throws metody przedefiniowywanej. 
 
Na przykład 
 

int readByte(String fileName) 
    throws IOException 

    try { 
        FileInputStream inp = new FileInputStream(fileName); 
        try { 
            return inp.read(); 
        } 
        catch(IOException e) { 
            System.out.println("File " + fileName + 
                               " empty or read error"); 
            throw new IOException(); 
        } 
    } 
    catch(FileNotFoundException e) { 
        System.out.println("File " + fileName +  
                           " does not exist"); 
        return -1; 
    } 

     

Podczas wykonania operacji 
 

new FileInputStream(fileName) 

 
może zostać wysłany wyjątek klasy FileNotFoundException albo wyjątek klasy IOException
 
Wysłanie  pierwszego  z  nich  jest  obsłużone  przez  zewnętrzną  frazę  catch,  a  o  możliwości  wysłania  drugiego 
informuje fraza throws
 
Podczas wykonywania operacji 
 

inp.read() 

 
może być wysłany wyjątek klasy IOException. Wyjątek ten jest obsłużony przez wewnętrzną frazę catch
 
Ponieważ w ciele funkcji readByte użyto instrukcji 
 

throw new IOException(); 

 
więc w jej nagłówku musi wystąpić fraza throws
 
 

Osłabienie wymagań 

 
Wymaganie użycia frazy throws nie dotyczy wyjątków kategorii RuntimeException, w tym wyjątków 
 
 

ArithmeticException 

(ąd operacji arytmetycznej

 

NumberFormatException 

(zły format liczby

background image

 

17 

 

IndexOutOfBoundsException 

(indeks poza zakresem

 

NegativeArraySizeException 

(ujemny rozmiar tablicy

 

NullPointerException 

(odwołanie przez odniesienie puste

 
oraz wyjątków kategorii Error, w tym wyjątków 
 
 

OutOfMemoryError 

(brak pamięci operacyjnej

 

StackOverflowError 

(przepełnienie stosu

 

ClassFormatError 

(ąd reprezentacji klasy

 

NoClassDefFoundError 

(brak definicji klasy

 
ale  dotyczy  wyjątków  kategorii  IOException  związanych  z  wykonywaniem  operacji  wejścia-wyjścia,  w  tym 
m.in. wyjątków 
 
 

EOFException 

(koniec pliku

 

FileNotFoundException 

(brak pliku

 

InterruptedIOException 

(przerwanie przesłania)   

 
Przykład Obsługiwanie wyjątków 
 

import java.io.*; 
 
public 
class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
        open("SOURCE", 0); 
        System.in.read(); 
    } 
    static void open(String fileName, int value) 
    { 
        // nast

ę

pna instrukcja jest poprawna, mimo i

ż

  

        // nie przewidziano dla niej obsługi wyj

ą

tku 

        // ArithmeticException 
        System.out.println(1 / value); 
        // ... 
        FileInputStream stream; 
        // nast

ę

pna instrukcja jest bł

ę

dna, poniewa

ż

 

        // nie przewidziano dla niej obsługi wyj

ą

tków 

        // FileNotFoundException i IOException 
        stream = new FileInputStream(fileName); 
    } 

 
Mimo iż podczas wykonywania instrukcji 
 

System.out.println(1 / value); 

 
może być wysłany wyjątek ArithmeticException, nie stawia się wymagania obsłużenia go. 
 
Podczas wykonania instrukcji 
 

stream = new FileInputStream(fileName); 

 
może zostać wysłany wyjątek FileNotFoundException oraz wyjątek IOException
 
Poniewa
ż żaden z tych wyjątków nie został obsłużony, więc przytoczony program jest ędny
 
Jednym  ze  sposobów  poprawienia  błędu  jest  użycie  instrukcji  try  dla  wyjątku  FileNotFoundException  oraz 
umieszczenie w nagłówku funkcji open frazy throws wyszczególniaj
ącej wyjątek IOException
 

import java.io.*; 
 

background image

 

18 

public 
class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
        open("SOURCE", 0); 
        System.in.read(); 
    } 
    static void open(String fileName, int value) 
        throws IOException 
    { 
        try { 
            System.out.println(1 / value); 
        }  
        catch(ArithmeticException e) { 
            System.out.println("Division by 0"); 
            System.exit(-1);  // przerwanie wykonywania 
        } 
        // ... 
        FileInputStream stream; 
        try { 
            stream = new FileInputStream(fileName); 
        } 
        catch(FileNotFoundException e) { 
            System.out.println("File" + fileName +  
                               " not found"); 
            System.exit(-2); 
        } 
    } 

 
Ponieważ  z  nagłówka  funkcji  open  wynika,  że  może  być  z  niej  wysłany  wyjątek  klasy  IOException,  wię
uwzgl
ędniono  to  w  funkcji  main.  Jako  metodę  obsługi  przyjęto  użycie  frazy  throws  wyszczególniającej  ten 
wyj
ątek. 
 
W celu usunięcia z nagłówka funkcji open frazy throws, należałoby instrukcję  
 

try { 
    stream = new FileInputStream(fileName); 

catch(FileNotFoundException e) { 
    System.out.println("File" + fileName +  
                       " not found"); 
    System.exit(-2); 

 
zastąpić na przykład instrukcją 
 

try { 
    stream = new FileInputStream(fileName); 

catch(FileNotFoundException e) { 
    System.out.println("File" + fileName +  
                       " not found"); 
    System.exit(-2); 

catch(IOException e) { 
    System.exit(-3); 

 

background image

 

19 

Część II 

Ś

rodowisko rozwojowe 

 
 
Do uruchomienia zamieszczonych w książce programów użyto Środowiska Rozwojowego Cafe  (wersja 1.51) 
składającego się z wbudowanego edytorakompilatora uruchamiacza. Środowisko to, opracowane przez firmę 
Symantec,  uzyskało  w  kategorii  narzędzi  do  programowania  w  Internecie  tytuł  Najlepszego  Produktu  Roku  
(The  best  product  for  Web  Development).  Ponieważ  na  tytuł  ten  w  pełni  zasługuje,  a  jest  znacznie  lepsze  niż 
inne porównywalne produkty (np. Visual J++), więc można je z przekonaniem polecić każdemu, komu zależy 
na komfortowym programowaniu w Javie. 
 
 

Wywołanie środowiska 

 
Po wywołaniu środowiska Cafe pojawia się informacja o jego wersji. Jest ona wyświetlana także po wydaniu 
polecenia Help/About. Na ekranie Winieta Cafe pokazano ekran identyfikujący środowisko. 
 
Ekran Winieta Cafe 

### welcome.gif 

 
 

Tworzenie programów 

 
Ś

rodowisko Cafe umożliwia tworzenie programów nie tylko w sposób tradycyjny, to jest metodą manualnego 

generowania  kodu,  ale  również  zawiera  mechanizmy  umożliwiające  programowanie  wizualne,  dostępne  w 
takich środowiskach drugiej generacji jak 
 
 

Visual Cafe 

(Symantec

 

Java WorkShop 

(Sun

 

Open JBuilder 

(Borland

 

SuperCede 

(Asymetrix

 

CodeWarrior 

(Metrowerks

 

Parts for Java 

(ParcPlace

 
Programowanie  wizualne  jest  bardzo  atrakcyjne  dla  tych,  którzy  nie  chcą  nadmiernie  zagłębiać  się  w  tajniki 
programowania w Javie, ale chcą w krótkim czasie uzyskać poprawnie działający i wcale niebanalny program. 
 
 
_________________________________________________________________________________________ 

Programowanie manualne 

 
Programowanie manualne polega na ręcznym przygotowaniu składników programu, a następnie utworzeniu z 
nich  wykonywalnego  programu  wynikowego.  Przy  takim  postępowaniu  wyposażenie  programu  w  oblicze 
graficzne 
(graphical interface) jest niekiedy dość uciążliwe, ale czyni program krótszym wydajniejszym
 
 

Edycja dokumentu 

 
Wydanie  polecenia  File/New  powoduje  utworzenie  okna  edycyjnego,  w  którym  można  przygotować  moduł 
ź

ródłowy albo dokument HTML. Po zakończeniu edycji moduł albo dokument należy zapamiętać w pliku. 

 
Jeśli tekst jest modułem źródłowym, który zawiera klasę publiczną Class, to nazwą pliku musi być Class.java
Jeśli jest dokumentem HTML, to nazwa pliku powinna mieć rozszerzenie .html
 
Uwaga: Jeśli program zawiera tylko jedną klasę publiczna Class, to zaleca się umieszczenie dokumentu HTML 
w pliku Class.html
 

background image

 

20 

 

Utworzenie projektu 

 
W  celu  utworzenia  projektu  należy  wydać  polecenie  Project/New.  Spowoduje  to  wyświetlenie  okna 
dialogowego Project Express pokazanego na ekranie Nazwanie projektu
 
Ekran Nazwanie projektu 

### name.gif 

 
W  klatce  Project  Name  należy  wówczas  podać  Nazwę  projektu,  a  przez  zapoznanie  się  z  listą  Directories 
upewnić, że plik projektu zostanie zapamiętany w uprzednio utworzonym katalogu. 
 
Po naciśnięciu przycisku Next wyświetli się okno pokazane na ekranie Określenie typu projektu.  
 
Ekran Określenie typu projektu 

### type.gif 

 
W  oknie  tym  należy  podać  czy  tworzony  program  jest  aplikacją  czy  apletem  (lista  Target  Type),  a  przez 
wybranie  nastawy  Project  Settings  zdecydować  się  na  to,  czy  program  wynikowy  będzie  uruchamiany 
(Debug) czy dystrybuowany (Release). 
 
Po naciśnięciu przycisku Next wyświetli się okno pokazane na ekranie Dodaj pliki. W oknie tym należy podać 
jakie pliki wchodzą w skład projektu. Odbywa się to przez dwukliknięcie elementu listy File Name, albo przez 
zaznaczenie jej elementu i naciśnięcie przycisku Add
 
Uwaga: Jeśli projekt dotyczy apletu, to do projektu należy również włączyć plik zawierający dokument HTML
 
Ekran Dodaj pliki 

### addfile.gif 

 
Po naciśnięciu przycisku Next wyświetli się okno pokazane na ekranie Ustawienia początkowe. W jego klatce 
Class Path można podać nazwy katalogów, w których mają być poszukiwane pliki włączone do projektu.  
 
Uwaga: Jeśli klasy programu należą do pakietu domyślnego (w pliku źródłowym nie ma polecenia package), 
to klatka Class Path może pozostać pusta). 
 
Ekran Ustawienia początkowe 

### settings.gif 

 
 

Otwarcie projektu 

 
Uprzednio  utworzony  projekt  może  być  następnie  otwarty,  a  przetwarzanie  programu  może  być  wówczas 
kontynuowane.  
 
W  celu  otwarcia  projektu  należy  wydać  polecenie  Project/Open.  Spowoduje  to  wyświetlenie  okna  Open 
Project
,  pokazanego  na  ekranie  Otwarcie  projektu.  W  jego  klatce  File  Name,  z  uwzględnieniem  katalogu 
określonego przez listę Folders, należy podać nazwę uprzednio utworzonego projektu. 
 
Ekran Otwarcie projektu  

### openprj.gif 

 
 

Modyfikowanie projektu  

 
W  celu  zmodyfikowania  otwartego  (a  więc  już  istniejącego)  projektu,  należy  wydać  polecenie  Project/Edit 
albo  Project/Settings.  Spowoduje  to  wyświetlenie  znanych  już  okienek  dialogowych,  w  których  można 
dokonać modyfikacji projektu (np. przekształcić aplikację w aplet, lub odwrotnie). 
 

background image

 

21 

 

Konfigurowanie pulpitu 

 
Przed,  albo  po  utworzeniu  projektu  można  skonfigurować  pulpit  środowiska.  Można  to  wykonać  za  pomocą 
poleceń  menu  Window.  W  szczególności  wydanie  poleceń  Views,  Build  i  Debug  spowoduje  wyświetlenie 
okienek pokazanych na ekranie Okienka Środowiska
 
Uwaga: Operacje na okienku Views odbywają się przez przeciągnięcie ikony, a operacje na okienku Builds i 
Debug odbywają się przez kliknięcie
 
Ekran Okienka Środowiska 

### desktop.gif 

 
 

Kompilowanie modułu 

 
W  celu  skompilowania  modułu  źródłowego  należy  w  okienku  Build  kliknąć  ikonę  Compile.  Jeśli  moduł 
zawiera  błędy,  to  kompilator  wyświetli  je  w  odrębnym  oknie.  Dwukliknięcie  wiersza  wyszczególniającego 
błąd spowoduje wyświetlenie tego fragmentu programu, w którym błąd ten wykryto. 
 
Uwaga:  Skompilowanie  pojedynczego  modułu  jest  wykonywane  rzadko.  Zazwyczaj  od  razu  przystępuje  się 
do  zbudowania  programu.  W  takim  wypadku  Cafe  kompiluje  tylko  te  moduły,  które  zostały  zmienione  w 
okresie od poprzedniej ich kompilacji. 
 
Na  ekranie  Niepomyślna  kompilacja  pokazano  skutek  skompilowania  programu  zawierającego  błędy 
składniowe. 
 
Ekran Niepomyślna kompilacja 

### badcomp.gif 

 
 

Budowanie programu  

 
W celu zbudowania programu należy w okienku Build kliknąć ikonę Build albo Rebuild All. Nastąpi wówczas 
skompilowanie  wszystkich  modułów  wchodzących  w  skład  projektu  i  utworzenie  B-kodu  programu. 
Sygnalizowanie ewentualnych błędów składniowych odbywa tak jak podczas kompilowania modułu. 
 
Budowanie może dotyczyć tylko takiego programu, w którym wyróżniono moduł główny (plik zawierający opis 
apletu  albo  program  z  funkcją  main).  Ma  to  istotne  znaczenie  zwłaszcza  wówczas,  gdy  w  skład  projektu 
wchodzi więcej niż jeden moduł źródłowy albo dokument HTML. W takim wypadku należy w oknie projektu 
kliknąć  lewym  przyciskiem  myszki  nazwę  modułu,  a  następnie  kliknąć  ją  prawym  i  wybrać  opcję  Mark  as 
Main
. Wykonanie tej operacji pokazano na ekranie Zdefiniowanie modułu głównego
 
Ekran Zdefiniowanie modułu głównego 

### main.gif 

 
Uwaga: Podczas kompilowania lub budowania programu pojawia się niekiedy komunikat 
 

 

Access violation 

 
Ten  niepokojący  komunikat,  któremu  nie  towarzyszy  żadna  diagnoza  o  przyczynie  błędu,  jest  najczęściej 
wywołany literówką w nazwie klasy, na przykład  napisaniem 
 

new DEbug(); 

 
zamiast 
 

new Debug(); 

 

background image

 

22 

albo doprowadzeniem do wystąpienia niezrównoważonych nawiasów klamrowych
 
Na  ekranie  Zagrożenie  bezpieczeństwa  pokazano  sytuację,  która  doprowadziła  do  powstania  rozpatrywanego 
komunikatu. 
 
Ekran Zagrożenie bezpieczeństwa 

### security.gif 

 
 

Dostarczenie argumentów 

 
Jeśli program jest aplikacją, to zazwyczaj oczekuje argumentów skojarzonych z parametrami funkcji main. W 
celu  ich  dostarczenia,  należy  wydać  polecenie  Project/Arguments.  Spowoduje  to  wyświetlenie  dialogu  Run 
arguments
,  pokazanego  na  ekranie  Dostarczenie  argumentów.  W  okienku  dialogu  można  podać  wymagane 
argumenty aplikacji
 
Ekran Dostarczenie argumentów 

### giveargs.gif 

 
 

Wykonanie programu 

 
W  celu  wykonania  programu  należy  w  okienku  Build  kliknąć  ikonę  Excecute  Program.  Jeśli  program  jest 
aplikacją, to nastąpi wówczas podjęcie wykonania publicznej i statycznej funkcji main. Jeśli jest apletem, to 
nastąpi  zinterpretowanie  dokumentu  HTML  wchodzącego  w  skład  projektu  i  wyświetlenie  opisanych  w  nim 
apletów. 
 

  Jeśli po aktywowaniu programu nastąpi wyładowanie i załadowanie Cafe, bez możliwości zaobserwowania 

czegokolwiek, to zapewne 
 
   1) 

Program jest aplikacją, ale nie wyposażono go w statyczną i publiczną funkcję main zawartą w klasie, 

która jest publiczna. 

 

2) 

Program  jest  aplikacją,  która  wyprowadza  wyniki  na  konsolę,  ale  nie  zapewniono  możliwości 

obejrzenia konsoli (zazwyczaj pomaga System.in.read()). 

 

3) 

Program jest apletem, ale nie zawiera publicznej nadklasy klasy Applet

 

4) 

Program jest apletem, ale jego nazwa podana w opisie apletu (np. Master.class) nie wynika z nazwy 

klasy apletu (np. Master). 

 

5) 

Wykonanie programu doprowadziło do wysłania nieobsłużonego wyjątku. 

 

  Jeśli po aktywowaniu apletu zniknie środowisko Cafe, ale nic się nie wyświetli, to najprawdopodobniejszą 

tego  przyczyną  jest  błąd  w  opisie  apletu,  na  przykład  brak  zamykającego  nawiasu  kątowego  w  opisie  apletu 
albo błędna nazwa parametru (np. lenght zamiast length). 
 
Uwaga: W systemie Windows 95 należy w takim wypadku nacisnąć klawisze Ctrl-Alt-Del, wyszukać program 
Appletviewer, a następnie wydać polecenie End Task. Spowoduje to powrót do środowiska Cafe
 

  Jeśli  po  wykonaniu  aplikacji  nie  pojawi  się  oczekiwany  napis,  to  zapewne  nie  spowodowano  wymiecenia 

bufora wyjściowego konsoli, tj. zapomniano, że po wykonaniu sekwencji instrukcji 
 

 

System.out.print(exp); 

 
należy wykonać instrukcję 
 

 

System.out.println(); 

 
 

Uruchomienie programu 

 

background image

 

23 

Podczas  uruchamiania  programów  posługiwałem  się  własną  klasa  uruchomieniową,  przytoczoną  w  Dodatku 
Klasa uruchomieniowa
. Okazała się ona bardzo przydatna, dlatego szczerze zachęcam do jej użycia. Ci, który 
oczekują  większego  komfortu  mogą  użyć  uruchamiacza  wbudowanego  w  Cafe.  Posługiwanie  się  nim  jest  na 
tyle proste, że nie wymaga dodatkowych wyjaśnień. 
 
Na ekranie Uruchamianie pokazano wykonanie programu posługującego się klasą Debug. Wyświetlenie jego 
modułu  źródłowego  zapewniono  dzięki  aktywowaniu  dodatkowej  kopii  środowiska  Cafe,  w  którym  otwarto 
plik zawierający ten moduł. 
 
Ekran Uruchamianie 

### debugger.gif 

 
Przykład Wysłanie nieobsłużonego wyjątku 
 

import java.io.IOException; 
import java.awt.*; 
 
public  
class Master { 
    static {  
        new Debug();    // por. Dodatek Klasa uruchomieniowa 
    } 
    public static void main(String args[]) 
    { 
        div(1, 0); 
        System.in.read(); 
    } 
    static void div(int num, int den) 
    { 
        System.out.println(num / den); 
    } 

 
Wykonanie  programu  powoduje  wysłanie  wyjątku  klasy  ArithmeticException,  a  na  skutek  nie  obsłużenia  go, 
wyprowadzenie do okna konsoli napisu 
 

java.lang.ArithmeticException: / by zero 
               at Master.div(Master.java:17) 
               at Master.main(Master.java:12) 

 
Z analizy napisu wynika, że błąd "dzielenie przez 0" (/ by zero) wystąpił w 17. wierszu funkcji sub, wywołanej z 
12. wiersza funkcji main
 
W wykryciu bł
ędu okazała się pomocna klasa Debug opisana w Dodatku Klasa uruchomieniowa
 
 
________________________________________________________________________________________ 

Programowanie wizualne 

 
Programowanie  wizualne  polega  na  użyciu  generatora  do  wytworzenia  kodu  obsługującego  graficznie 
zaprojektowane oblicze programu. Wygenerowanie programu jest łatwe, ale na ogół czyni program dłuższym 
mniej wydajnym.  
 
Następujący  aplet,  utworzony  za  pomocą  generatora  programów,  pokazany  podczas  wykonania  na  ekranie 
Programowanie  wizualne,  umożliwia  zapoznanie  się  z  kolorami  o  podanych  składnikach  RGB  (red,  green
blue). 
 
Ekran Programowanie wizualne 

### visual.gif 

 
Przykład Program wygenerowany 
 

<HTML> 

background image

 

24 

<HEAD><TITLE> Master </TITLE></HEAD> 
<BODY> 
<APPLET CODE="Master.class" WIDTH=283 HEIGHT=190> 
</APPLET> 
</BODY> 
</HTML> 
================================================= 

 

import java.applet.*; 
import java.awt.*; 
 
public class Master extends Applet { 
    private int red, green, blue; 
    public void init() 
    { 
        super.init(); 
        //{{INIT_CONTROLS 
        setLayout(null); 
        resize(303, 133); 
        panel = new Panel(); 
        panel.setLayout(null); 
        add(panel); 
        panel.reshape(14, 39, 66, 43); 
        redEdit = new TextField(7); 
        add(redEdit); 
        redEdit.reshape(106, 18, 58, 20); 
        greenEdit = new TextField(7); 
        add(greenEdit); 
        greenEdit.reshape(106, 54, 58, 20); 
        blueEdit = new TextField(7); 
        add(blueEdit); 
        blueEdit.reshape(106, 90, 58, 20); 
        redLabel = new Label("Red"); 
        add(redLabel); 
        redLabel.reshape(161, 20, 47, 17); 
        greenLabel = new Label("Green"); 
        add(greenLabel); 
        greenLabel.reshape(161, 56, 47, 17); 
        blueLabel = new Label("Blue"); 
        add(blueLabel); 
        blueLabel.reshape(161,92,47,17); 
        exitButton = new Button("Exit"); 
        exitButton.setFont(new Font("Helvetica",  
                                    Font.BOLD,10)); 
        add(exitButton); 
        exitButton.reshape(232, 3, 67, 19); 
        //}} 
    } 
    public void paint(Graphics gDC) 
    { 
        Rectangle b = panel.bounds(); 
        gDC.drawRect(b.x-1, b.y-1,  
                     b.width+1, b.height+1); 
    } 
    public boolean handleEvent(Event evt) 
    { 
        if (evt.id == Event.ACTION_EVENT &&  
            evt.target == redEdit) { 
                redEnter(evt); 
                return true; 
        } 
        else if (evt.id == Event.ACTION_EVENT &&  
                 evt.target == greenEdit) { 
                greenEnter(evt); 
                return true; 
        } 
        else if (evt.id == Event.ACTION_EVENT &&  
                 evt.target == blueEdit) { 
                blueEnter(evt); 
                return true; 
        } 
        else if (evt.id == Event.ACTION_EVENT &&  

background image

 

25 

                 evt.target == exitButton) { 
                clickedExitButton(); 
                return true; 
        } 
        return super.handleEvent(evt); 
    } 
 
    //{{DECLARE_CONTROLS 
    Panel panel; 
    TextField redEdit; 
    TextField greenEdit; 
    TextField blueEdit; 
    Label redLabel; 
    Label greenLabel; 
    Label blueLabel; 
    Button exitButton; 
    //}} 
    public void clickedExitButton() 
    { 
        // to do: put event handler code here. 
        System.exit(0); 
    } 
    public void redEnter(Event evt) 
    { 
        // to do: put event handler code here. 
                showColor(red = rgb(evt), green, blue); 
    } 
    public void greenEnter(Event evt) 
    { 
        // to do: put event handler code here. 
                showColor(red, green = rgb(evt), blue); 
    } 
    public void blueEnter(Event evt) 
    { 
        // to do: put event handler code here. 
        showColor(red, green, blue = rgb(evt)); 
    } 
    int rgb(Event evt) 
    { 
        return Integer.parseInt((String)evt.arg); 
    } 
    void showColor(int r, int g, int b) 
    { 
        Color color = new Color(r, g, b); 
        panel.setBackground(color); 
        panel.repaint(); 
    } 

 
 

Generowanie programu 

 
W  celu  wywołania  generatora  należy  wydać  polecenie  Tools/AppExpress.  Spowoduje  to  wyświetlenie  okna 
dialogowego pokazanego na ekranie Generator programu
 
Bezpośrednio po wywołaniu generatora jest w nim wyróżniony przycisk Application Type oraz związany z nim 
zestaw przycisków Applications. Umożliwiają one określenie, czy program ma być apletem czy aplikacją. 
 
Uwaga:  Dalszy  opis  dotyczy  generowania  apletu  zdefiniowanego  przez  klasę  o arbitralnie wybranej  nazwie 
Master
 
Ekran Generator programu 

### apptype.gif 

 
Po naciśnięciu przycisku Select Directory wyświetli się okno pokazane na ekranie Wybranie katalogu. Należy 
w nim określić nazwę katalogu, w którym ma być wygenerowany program. 
 
Ekran Wybranie katalogu 

background image

 

26 

### catalog.gif 

 
Po  naciśnięciu  przycisku  Miscellaneous  wyświetli  się  okno  pokazane  na  ekranie  Nazwa  projektu.  Należy  w 
nim podać nazwę projektu (Master). Odbywa się to przez wypełnienie klatki Project Name 
 
Ekran Nazwa projektu 

### prjname.gif 

 
Po naciśnięciu przycisku Finish nastąpi wygenerowanie programu aplikacji. 
 
 

Projektowanie oblicza 

 
W  celu  zaprojektowania  oblicza  graficznego  programu  należy  wydać  polecenie  Resource/Open,  a  w 
wyświetlonym  wówczas  oknie  dialogowym  wybrać  nazwę  pliku  z  rozszerzeniem .rc  (Master.rc). Spowoduje 
to  wyświetlenie  okna,  w  którym  po  kliknięciu  pozycji  Form  i  Master.init  pojawi  się  ramka,  pokazana  na 
ekranie Pulpit apletu
 
Ekran Pulpit apletu 

### pulpit.gif 

 
Zapełnienie  ramki  sterownikami  odbywa  się  za  pomocą  narzędzi  zawartych  w  ramce  narzędziowej.  Po 
kliknięciu  narzędzia  i  przeciągnięciu  kursora  w  obrębie  pulpitu  następuje  wykreślenie  sterownika.  Zestawy 
sterowników  mogą  być  rozmieszczane  na  pulpicie  za  pomocą  narzędzi  wyszczególnionych  na  pasku 
narzędziowym okna zasobów. 
 
Na ekranie Sterowniki apletu pokazano przykładowe rozmieszczenie sterowników LabelTextField Panel
 
Ekran Sterowniki apletu 

### controls.gif 

 
 

Określanie właściwości 

 
Po naniesieniu sterowników i pojemników na pulpit, należy określić ich właściwości: nazwy i opisy (Member
Caption), wyglą(Look) i reakcje (Events). 
 
Na ekranach Nazwa przyciskuWygląd przycisku i Reakcja przycisku pokazano sposób określenia właściwości 
dla przycisku opatrzonego napisem Exit
 
Ekran Nazwa przycisku 

### member.gif 

 
Ekran Wygląd przycisku 

### look.gif 

 
Ekran Reakcja przycisku 

### events.gif 

 
Po wybraniu zdarzenia dotyczącego sterownika (np. clicked) i naciśnięciu przycisku Edit, zostanie wyświetlone 
okno  Class  Editor,  pokazane  na  ekranie  Zdefiniowanie  obsługi.  W  oknie  tym  można  określić  sposób 
reagowania sterownika na zdarzenie. 
 
Ekran Zdefiniowanie obsługi 

### handler.gif 

 
 

Komponowanie programu 

 

background image

 

27 

Po  wykonaniu  podanych  czynności  dla  wszystkich  komponentów  pulpitu,  a  następnie  zamknięciu  okna  do 
projektowania zasobów, otrzymuje się gotowy program, który może być poddany dodatkowej ręcznej obróbce 
(m.in. skopiowaniu komentarza zawierającego opis apletu do pliku z rozszerzeniem .html). 
 
Wygenerowany  wizualnie  program  należy  oczywiście  zbudować,  uruchomić  i  wytestować.  Czynności  te 
wykonuje się w identyczny sposób jak dla programu utworzonego ręcznie. 
 
 

background image

 

28 

Część III 

Podstawy programowania 

 
Program  źródłowy  jest  zestawem  napisów  umieszczonych  w  modułach  źródłowych.  Każdy  moduł  źródłowy 
jest umieszczony w pliku.  
 
Moduł  źródłowy  składa  się  z  komentarzy,  deklaracji  pakietudeklaracji  importu  i  definicji klas. Jeśli moduł 
ź

ródłowy zawiera definicję więcej niż jednej klasy, to tylko jedna z nich może być publiczna. Klasa publiczna o 

nazwie Class musi być umieszczona w pliku o nazwie Class.java
 
Po  skompilowaniu  modułu  źródłowego  powstaje  moduł  wynikowy  z  rozszerzeniem  .class.  Z  takich  właśnie 
modułów, po uzupełnieniu ich modułami bibliotecznymi pochodzącymi z pliku classes.zip tworzy się program 
wynikowy

 
 
_________________________________________________________________________________________ 

Leksemy 

 
Moduł  źródłowy  składa  się  z  odstępów  i  komentarzy  oraz  z  sekwencji  leksemów:  słów  kluczowych
identyfikatorówoperatorów ograniczników.  
 
 

Słowa kluczowe 

 
Słowo  kluczowe  jest  napisem  służącym  do  zapisywania  instrukcji  programu.  Do  najczęściej  używanych  słów 
kluczowych należą 
 

 

int  double  

 

if   else   while   for  

 
Na przykład 
 

int age;            // wiek 
    // ... 
if(age >= 18)       // je

ś

li sko

ń

czyłe

ś

 18 lat 

    youCanVote();   // mo

ż

esz głosowa

ć

 

else                // w przeciwnym razie 
    youMustWait();  // musisz zaczeka

ć

 

 

Deklaracja zmiennej age zawiera słowo kluczowe int
 
Instrukcja warunkowa zawiera słowa kluczowe if i else
 
 

Literały 

 
Literałem  jest  nazwa  zmiennej,  której  wartość  i  typ  wynikają  z  zapisu  literału.  Literałami  są  liczby  całkowite 
(np. 12), liczby rzeczywiste (np. 1.2e+2), znaki (np. ('a') i łańcuchy (np. "Yes"). 
 
 

Liczby całkowite 

 
Liczba  całkowita  dziesiętna  składa  się  z  ciągu  cyfr  dziesiętnych.  Liczba  całkowita  szesnastkowa  składa  się  z 
napisu 0x po którym następuje ciąg cyfr szesnastkowych. Wartości liczb typu "int" należą do przedziału 
 

 

0 .. +9,223,372,036,854,775,807 

 
Na przykład 
 

background image

 

29 

 

12 

liczba całkowita dziesiętna 

(wartość: 12

 

0xc  

liczba całkowita szesnastkowa 

(wartość: 12

 

-12 

wyrażenie całkowite 

 
 

Liczby rzeczywiste 

 
Liczba  rzeczywista  składa  się  z  dwóch  ciągów  cyfr  dziesiętnych  oddzielonych  kropką,  po  których  może 
wystąpić litera e z wykładnikiem.  
 
Ciąg  cyfr  przed  albo  po  kropce  może  być  pominięty.  Kropkę  można  pominąć  tylko  wówczas,  gdy  liczbę 
zapisano  z  wykładnikiem  albo  gdy  po  liczbie  występuje  litera  f.  Wykładnik  ma  postać  liczby  całkowitej, 
ewentualnie poprzedzonej znakiem + (plus) albo - (minus). 
 
Wartości liczb typu "double" należą do przedziału 
 

 

0 .. 1.8e308 

 
Na przykład 
 
 

1.2 

liczba rzeczywista 

(wartość: 1.2

 

3e-2 

liczba rzeczywista 

(wartość: 0.03

 

-1.2 

wyrażenie rzeczywiste 

 
 

Znaki 

 
Literał  znakowy  ma  postać  'c'  w  której  c  jest  znakiem  widocznym  (np.  'a'),  symbolem  znaku  (np.  '\n'),  albo 
szesnastkowym  kodem  znaku  (np.  '\u0061').  Wartością  literału  znakowego  jest  kod  opisanego  przezeń  znaku 
(np. wartością literału 'a' jest 97). 
 
W tabeli Symbole znaków wymieniono kilka ważniejszych literałów znakowych. 
 
Tabela Symbole znaków 

### 

 

Znak 

Interpretacja 

 
 

'\n' 

znak nowego wiersza 

 

'\r' 

znak powrotu karetki 

 

'\0' 

znak o kodzie 0 

 

'\t' 

znak tabulacji 

 

'\\' 

znak \ 

 

'\'

znak ' 

(krócej: ''') 

 

'\"' 

znak "  (krócej: '"') 

### 

 

 

 

Łańcuchy 

 
Łańcuchem jest napis "cc ... c", w którym każde c jest takim samym napisem jak w literale znakowym. Literał 
łańcuchowy  składający  się  z  N  znaków  jest  nazwą  odnośnika  do  wektora  o  N+1  elementach  typu  "char", 
zainicjowanych kodami kolejnych znaków łańcucha oraz dodatkowo kodem znaku '\0' (liczbą 0). 
 
Na przykład 
 
 

"Yes" 

nazwa odnośnika do wektora zainicjowanego 

 

 

kodami znaków Yeoraz kodem znaku '\0' (liczbą 0

 

"" 

nazwa odnośnika do 1-elementowego wektora 

 

 

zainicjowanego kodem znaku '\0' 

background image

 

30 

 
 

Identyfikatory 

 
Identyfikator  jest  napisem  reprezentującym  element  programu:  pakiet,  klasę,  procedurę  albo  zmienną. 
Identyfikatory mogą składać się tylko z liter cyfr (dolar podkreślenie są uznawane za litery), ale nie mogą 
zaczynać się od cyfry.  
 
Liczba znaków identyfikatora nie jest ograniczona. Identyfikator nie możskładać się z takich samych znaków 
jak słowo kluczowe. 
 
Identyfikatorami są m.in. napisy 
 

 

forSale   ForSale   forsale  For_Sale   $forSale 

 
Nie jest identyfikatorem napis 
 

 

4sale 

 
 

Operatory 

 
Operatorami  są  ciągi  znaków,  służące  do  zapisywania  operacji.  Do  najczęściej  używanych  operacji  należą 
m.in.: 
 

operacje arytmetyczne 

 
 

+ 

(dodawanie),   

- 

(odejmowanie),    

 

* 

(mnożenie), 

/ 

(dzielenie),   

 

++  (zwiększenie o 1

-- 

(zmniejszenie o 1

 

+=  (dodanie

-= 

(odjęcie

 

*= 

(pomnożenie

/= 

(podzielenie

 

porównania 

 
 

==  (równe

!= 

(nie równe), 

 

< 

(mniejsze

> 

(większe), 

 

<=  (mniejsze lub równe

>=  (większe lub równe

 

operacje logiczne 

 
 

! 

(negacja

 

&&  (koniunkcja

|| 

(dysjunkcja

 

Na przykład 
 

int num = 5, var = 7; 
var += -num++ / 2; 
System.out.println(var);    // 5 
System.out.println(num);    // 6 

 
Instrukcja 
 

var += -num++ / 2; 

 
zawiera operatory 
 

 

++   -   /   += 

 

background image

 

31 

Wartości kolejnych podwyrażeń są następujące 
 

 

num 

 

num++ 

 

-num++ 

-5 

 

-num++ / 2 

-2 

 
A zatem do zmiennej var dodaje się -2, czyli nadaje się jej wartość 5
 
 

Ograniczniki 

 
Ogranicznikami  są  pojedyncze  znaki  oddzielające  albo  kończące  elementy  programu.  Najczęściej  używanymi 
ogranicznikami są przecinki i średniki
 
Na przykład 
 

for(int suma = 0, i = 0; i < 100 ; i++) 
    suma = suma + i; 
 

Przytoczona instrukcja zawiera 6 ograniczników (dwa nawiasy, przecinek i trzy średniki). 
 
 
_________________________________________________________________________________________ 

Zmienne 

 
Zmienna  jest  elementem  programu  wynikowego.  W  programie  źródłowym  występuje  pod  postacią 
identyfikatora (np. var), literału (np. 123.4'a'"Hello"), albo wyrażenia (np. a+1). 
 
Zmienne mogą być modyfikowalne albo niemodyfikowalne. Zmiennym modyfikowalnym można, a zmiennym 
niemodyfikowalnym nie można przypisywać danych. Zmienne niemodyfikowalne mogą być nazywane stałymi
 
Uwaga:  Zapis  literału  uznaje  się  za  niejawną  deklarację  reprezentowanej  przezeń  zmiennej.  Wszystkie 
pozostałe zmienne muszą być zadeklarowane jawnie, albo muszą powstać jako rezultat wykonania operacji na 
zmiennych. W szczególności każde wyrażenie jest chwilową nazwą pewnej zmiennej. 
 
Na przykład 
 

int var; 
// ... 
var = var + 2; 

 
Identyfikator var jest nazwą zmiennej typu "int". 
 
Wyrażenie  
 

 

var + 2  

 
jest chwilową nazwą rezultatu operacji dodawania. 
 
Wyrażenie  
 

 

var = var + 2 

 
jest chwilową nazwą rezultatu operacji przypisania. 
 
 

Identyfikowanie zmiennych 

 

background image

 

32 

Do identyfikowania zmiennych służą ich nazwy. Nazwą zmiennej modyfikowalnej jest identyfikator, a nazwą 
zmiennej niemodyfikowalnej literał. Wyrażenie jest nazwą zmiennej modyfikowalnej tylko wówczas, gdy jest l-
nazw
ą.  
 
O  tym,  czy  rezultat  operacji  jest  czy  nie  jest  l-nazwą  decyduje  opis  operacji.  Decyzje  takie  zostały  podjęte 
arbitralnie  przez  twórców  języka.  W  szczególności  jest  l-nazwą  każdy  identyfikator  zmiennej  (z  wyjątkiem 
odnośnika  this),  ale nie  jest nią wyrażenie arytmetyczne (np. 2  + 3), ani nazwa rezultatu przypisania (np. a = 
12

 
Uwaga: Określenie l-nazwa bierze się stąd, że tylko taka nazwa zmiennej może wystąpić jako lewy argument 
operacji przypisania. 
 
Na przykład 
 

int var = 12;  
++++var;         // bł

ą

d (++var nie jest l-nazw

ą

(var = 2) = 3;   // bł

ą

d (var = 2 nie jest l-nazw

ą

this = null      // bł

ą

d (this nie jest l-nazw

ą

 
 

Modyfikowanie zmiennych 

 
Modyfikowanie zmiennych odbywa się za pomocą operacji przypisania oraz za pomocą operacji wejścia. 
 
Na przykład 
 

int chr, fix; 
fix = 'a';              
// ... 
chr = System.in.read();  
 

Zmienna fix jest modyfikowana za pomocą operacji przypisania, a zmienna chr jest modyfikowana za pomocą 
operacji wej
ścia. 
 
 

Przekształcanie zmiennych 

 
Do  przekształcenia  zmiennej  w  inną  zmienną  służy  konwersja.  Konwersja,  która  może  być  zastosowana 
niejawnie jest konwersją standardową.  
 
Konwersja  standardowa  jest  konwersją  dopuszczalną  (poprawną  składniowo),  ale  nie  musi  być  konwersją 
wykonalną (poprawną semantycznie). 
 
Wyrażenie poddane konwersji zapisuje się w postaci 
 

 

(Type)exp 

 
w  której  Type  jest  nazwą  typu  docelowego  (najczęściej  identyfikatorem  typu),  a  exp  jest  wyrażeniem,  które 
można poddać konwersji do typu "Type". 
 
Uwaga: Nazwa zmiennej, która jest rezultatem konwersji nie jest l-nazwą. 
 
Na przykład 
 

int num = (int)12.0;  // dobrze (jawna konwersja) 
double dbl = 12;      // dobrze (niejawna konwersja) 
int fix = 12.0;       // bł

ą

d (brak jawnej konwersji) 

(int)fix = 12;        // bł

ą

d ((int)Fix nie jest l-nazw

ą

 
 

Promocje 

background image

 

33 

 
Przed wykonaniem pewnych operacji jednoargumentowych jest wykonywana promocja do typu ogólniejszego 
(np. z typu "char" do typu "int"). Takie przekształcenie jest wykonywane niejawnie
 
Na przykład 
 

char chr = 'a'; 
System.out.print(chr);       // a 
System.out.print((int)chr);  // 97 
System.out.print(+chr);      // 97 

 
Przed wykonaniem operacji +chr dokonano promocji z typu "char" do typu "int". 
 
 

Typ wspólny 

 
Przed  wykonaniem  pewnych  operacji  dwuargumentowych  jest  wykonywane  przekształcenie  argumentów  do 
najwęższego  typu  ogólniejszego,  stanowiącego  wówczas  typ  wspólny  argumentów.  Przekształcenie  takie  jest 
wykonywane niejawnie
 
Na przykład 
 

char chr = 'a';                     //  0000 0000 0110 0001 
System.out.print((int)chr & 0x21);  //  33 
System.out.print(chr & 0x21);       //  33 

 
Przed wykonaniem operacji chr & 0x21 przekształcono argument chr do typu wspólnego "int". 
 
 
_________________________________________________________________________________________ 

Zmienne orzecznikowe 

 
Zmiennymi  orzecznikowymi  (krótko:  orzecznikami)  są  zmienne  typu  "boolean".  Zmiennym  orzecznikowym 
można przypisywać orzeczenia reprezentujące prawdę (true) albo fałsz (false). 
 
Na  orzecznikach  można  wykonywać  operacje  negacji  (!),  koniunkcji  (&&)  i  dysjunkcji  (||).  Operacja  negacji 
jest jednoargumentowa, a operacje koniunkcji i dysjunkcji są dwuargumentowe. 
 
 

Negacja 

 
Jeśli var jest orzecznikiem o wartości false, to rezultatem operacji !var jest orzecznik o wartości true
 
Jeśli var jest orzecznikiem o wartości true, to rezultatem operacji !var jest orzecznik o wartości false
 
Na przykład 
 

boolean var = false; 
var = !var; 

 
Po opracowaniu deklaracji, orzecznik var ma wartość false
 
Po opracowaniu przypisania, orzecznik var ma wartość true
 
 

Koniunkcja 

 
Koniunkcja dwóch argumentów ma wartość true tylko wówczas, gdy każdy z nich ma wartość true. Jeśli lewy 
argument ma wartość false, to rezygnuje się z opracowania prawego. 

background image

 

34 

 
Na przykład 
 

boolean one = true, 
        two = false; 
boolean var; 
var = one && two; 
two = var && fun(); 

 
Zmiennej var przypisano warto
ść false
 
Podczas wykonywania ostatniej instrukcji nie dojdzie do wykonania funkcji fun
 
 

Dysjunkcja 

 
Dysjunkcja dwóch argumentów ma wartość false tylko wówczas, gdy każdy z nich ma wartość false. Jeśli lewy 
argument ma wartość true, to rezygnuje się z opracowania prawego. 
 
Na przykład 
 

boolean one = true, 
        two = false; 
boolean var; 
var = one || fun(); 

 
Zmiennej var przypisano wartość true.  
 
Podczas wykonywania ostatniej instrukcji nie dojdzie do wykonania funkcji fun
 
 
_________________________________________________________________________________________ 

Zmienne arytmetyczne 

 
Zmiennymi arytmetycznymi są zmienne całkowite (np. typu "int") i rzeczywiste (np. typu "double"). 
 
Na  zmiennych  arytmetycznych  można  wykonywać  podstawowe  działania  arytmetyczne:  dodawanie
odejmowaniemnożenie dzielenie, operacje zwiększenia zmniejszenia wartości o 1 oraz operacje porówania
 
 

Zmienne całkowite 

 
Najczęściej  używanymi  zmiennymi  całkowitymi  są  4-bajtowe  zmienne  typu  "int".  Można  w  nich 
przechowywać dane liczbowe z przedziału 
 
 

-2,147,483,648 .. 2,147,483,647 

 
Rzadziej  są  używane  zmienne  całkowite  typu  "byte",  "short"  i  "long".  Zakresy  ich  wartości  przytoczono  w 
tabeli Pozostałe zmienne całkowite
 
Uwaga: Nie ma literałów typu "byte" i "short". Literał typu "long" ma postać literału typu "int" zakończonego 
literą L albo l (np. 12L). 
 
Tabela Pozostałe zmienne całkowite 

### 

 

Typ 

Rozmiar 

Wartości 

 
 

byte 

1-bajt 

(od   -128 do 127) 

 

short 

2-bajty 

(od 

-32768 do 32767) 

 

long 

8-bajtów 

(od 

-9,223,372,036,854,775,808 do 

background image

 

35 

 

 

 

 

+9,223,372,036,854,775,807) 

### 

 
 

Zmienne rzeczywiste 

Najczęściej  używanymi  zmiennymi  rzeczywistymi  są  8-bajtowe  zmienne  typu  "double".  Można  w  nich 
przechowywać dane liczbowe z przedziału 
 
 

 -1.8e308 .. 1.8e308 

 
Rzadziej są używane 4-bajtowe zmienne typu "float". Można w nich przechowywać dane liczbowe z przedziału 
 
  

-3.4e38 .. 3.4e38 

 
Uwaga: Literał typu "float" ma postać literału typu "int" albo "double" zakończonego literą F albo f (np. 12f). 
 
 

Podstawowe działania arytmetyczne 

 
Rezultatem podstawowego działania arytmetycznego jest zmienna arytmetyczna o wartości określonej przez to 
działanie. Jeśli operacja jest dwuargumentowa, a jeden z argumentów jest typu "double", to i rezultat operacji 
jest typu "double". 
 
Na przykład 
 

int fix = 12; 
double dbl = 3.4; 
double var; 
var = fix + dbl; 

 
Zmiennej var przypisano daną typu "double" o wartości 15.4
 
Uwaga:  Jeśli  zmienna  arytmetyczna  jest  połączona  operatorem  +  (plus)  ze  zmienną  łańcuchową,  to  zmienna 
arytmetyczna  jest  przekształcana  w  łańcuchową,  a  następnie  tworzy  się  zmienną  łańcuchową  składającą  się  z 
wszystkich znaków obu łańcuchów. 
 
W szczególności, rezultatem operacji 
 

 

"" + 12 

 
jest "12", rezultatem operacji  
 

 

"" + 2 + 3 

 
jest "23", a rezultatem operacji 
 

 

"Wynik: " + (2 + 3) 

 
jest  

 

"Wynik: 5" 

 
 

Operacje zwiększenia i zmniejszenia 

 
Rezultatem następnikowej operacji zwiększenia (++) jest zmienna o takiej wartości jaką ma argument operacji. 
Skutkiem ubocznym operacji zwiększenia jest dodanie do argumentu liczby 1
 
Rezultatem następnikowej operacji zmniejszenia (--) jest zmienna o takiej wartości jaką ma argument operacji. 
Skutkiem ubocznym operacji zmniejszenia jest odjęcie od argumentu liczby 1
 

background image

 

36 

Na przykład 
 

int fix = 12; 
int num = fix++;   
fix = num--; 
 

Tuż przed wykonaniem ostatniej instrukcji zmienna fix oraz zmienna num ma wartość 12
 
Tuż po wykonaniu ostatniej instrukcji zmienna fix ma wartoś12, a zmienna num ma wartość 11
 
 

Operacje porównania 

 
Rezultatem  operacji  porównania:  ==  (równe),  !=  (nie  równe),  <  (mniejsze),  >  (większe),  <=  (mniejsze  lub 
równe
), >=  (większe lub równe) jest orzecznik o wartości określającej prawdziwość porównania (np. 12 > 10 
ma wartość true). 
 
Uwaga: Specjalnej ostrożności wymaga użycie operatora równe. Jeśli zapomni się o tym, że składa się z pary 
znaków równości, to faktycznie stanie się operatorem przypisania
 
Na przykład 
 

int fix; 
// ... 
if(fix >= 100 && fix <= 200) 
    System.out.println("Warto

ść

 zmiennej fix " + 

                       "nale

ż

y do przedziału 100 .. 200"); 

if(fix == 100) 
    System.out.println("Zmienna fix ma warto

ść

 100"); 

 
Wykonanie instrukcji warunkowych powoduje wyprowadzenie napisu dotyczącego aktualnej wartości zmiennej 
fix
 
Gdyby w ostatniej instrukcji relacj
ę  
 

 

fix == 100 

 
omyłkowo zastąpiono operacją przypisania 
 

 

fix = 100 

 
to niezależnie od wartości zmiennej fix wyprowadzono by napis 
 

 

Zmienna fix ma warto

ść

 100 

 
 
_________________________________________________________________________________________ 

Zmienne znakowe 

 
 
Zmienne  znakowe  są  16-bitowymi  zmiennymi  arytmetycznymi.  Mogą  być  używane  w  taki  sam  sposób  jak 
pozostałe  zmienne  arytmetyczne,  ale  ich  głównym  przeznaczeniem  jest  reprezentowanie  kodów  znaków
zarówno znaków europejskich jak i znaków używanych w krajach azjatyckich. 
 
Dane znakowe są znakami Unikodu. Jego pierwszych 256 znaków jest identycznych ze znakami kodu ASCII 
Latin-1
. Wartości wszystkich znaków Unikodu są nieujemne
 
Na przykład 
 

char chr; 
// ... 

background image

 

37 

if(chr >= 'a' && chr <= 'z') 
    System.out.println("Kod przypisany zmiennej chr " + 
                       "jest kodem litery a .. z"); 
if(chr == 'm') 
    System.out.println("Kod przypisany zmiennej chr " + 
                       "jest kodem litery m"); 

 
Wykonanie instrukcji warunkowych powoduje wyprowadzenie napisu dotyczącego aktualnej wartości zmiennej 
chr
 
 
_________________________________________________________________________________________ 

Zmienne odnośnikowe 

 
Zmienne  odnośnikowe  służą  do  identyfikowania  zmiennych  obiektowych,  m.in.  zmiennych  predefiniowanego 
typu łańcuchowego "String".  
 
Identyfikowanie  odbywa  się  za  pomocą  odniesień  przypisywanych  odnośnikom.  Każda  taka  dana  zawiera 
informację o adresie i typie identyfikowanego przez nią obiektu 
 
Uwaga:  Odnośnikowi  dowolnego  typu  można  przypisać  odniesienie  puste  reprezentowane  przez  słowo 
kluczowe null. Po wykonaniu takiego przypisania odnośnik nie identyfikuje żadnego obiektu. 
 
 

Identyfikowanie zmiennych 

 
Zmienna  odnośnikowa  typu  "Class"  może  identyfikować  zmienne  klasy  Class  oraz  zmienne  dowolnej  jej 
podklasy.  
 
W  szczególności,  ponieważ  klasa  Applet  jest  podklasą  klasy  Panel,  więc  odnośnik  ref  zadeklarowany  za 
pomocą instrukcji 
 

 

Panel ref; 

 
może identyfikować zarówno zmienne klasy Panel jak i zmienne klasy Applet
 
Na odnośnikach nie wolno wykonywać innych operacji niż porównanie odnośników i przypisanie odniesienia. 
Jeśli  dwa  odnośniki identyfikują tę samą zmienną, to  rezultat operacji == (równe) ma wartość true, a rezultat 
operacji != (nie równe) ma wartość false
 
Na przykład 
 

String h = "Hello"; 
String w = "World"; 
System.out.println(h + " " + w); 

 
Odno
śnik  h  identyfikuje  obiekt  zainicjowany  łańcuchem  Hello,  a  odnośnik  w  identyfikuje  obiekt  zainicjowany 
ła
ńcuchem World
 
Rezultatem operacji 
 

 

h + " " + w 

 
jest odnośnik identyfikujący obiekt zainicjowany łańcuchem 
 

 

Hello World 

 
 
_________________________________________________________________________________________ 

Zmienne łańcuchowe 

background image

 

38 

 
Zmienną łańcuchową jest zmienna obiektowa predefiniowanego typu "String" albo "StringBuffer". Podobnie 
jak obiekty innych typów, zmienne łańcuchowe są identyfikowane przez odnośniki. 
 
Ponieważ  porównanie  odnośników  polega  jedynie  na  sprawdzeniu,  czy  identyfikują  one  te  same  obiekty, 
porównanie wartości zmiennych łańcuchowych może być wykonane tylko za pomocą specjalnych metod, takich 
jak equals compareTo
 
Uwaga:  Zasada  porównywania  łańcuchów  jest  następująca:  1)  jeśli  łańcuchy  składają  się  z  takich  samych 
znaków, to uznaje się je za równe, 2) w przeciwnym ignoruje się początkowe znaki łańcuchów (jeśli są parami 
identyczne), a porównanie łańcuchów zastępuje porównaniem pierwszej pary znaków nieidentycznych (znak o 
mniejszym kodzie uznaje się za mniejszy). 
 
 

public boolean equals(Object obj) 

Rezultatem metody equals użytej w wyrażeniu 
 

 

str1.equals(str2

 
jest orzecznik o wartości true jeśli odnośniki str1 str2 identyfikują zmienne łańcuchowe zainicjowane takimi 
samymi łańcuchami. W przeciwnym razie orzecznik ma wartość false
 
Na przykład 
 

String str = "$"; 
String one = str + "100"; 
String two = str + "100"; 
System.out.println(one == two);       // false 
System.out.println(one.equals(two));  // true 

 
 

public int compareTo(String str) 

Rezultatem metody compareTo użytej w wyrażeniu 
 

 

str1.compareTo(str2

 
jest  zmienna  typu  "int"  o  wartości  ujemnej,  zero,  albo  dodatniej,  odpowiednio  do  tego  czy  odnośniki  str1  
str2  identyfikują  zmienne  zainicjowane  takimi  łańcuchami,  że  pierwszy  jest  mniejszy  od  drugiego,  oba  są 
równe, albo pierwszy jest większy od drugiego. 
 
 
Na przykład 
 

String one; 
String two; 
// ... 
int res = one.compareTo(two); 
if(res < 0) 
    System.out.println("Pierwszy ła

ń

cuch jest mniejszy"); 

if(res == 0) 
    System.out.println("Ła

ń

cuchy s

ą

 równe"); 

if(res > 0) 
    System.out.println("Pierwszy ła

ń

cuch jest wi

ę

kszy"); 

 

Jeśli tuż przed użyciem metody compareTo wykonano instrukcje 
 

one = "ABC"; 
two = "Ab"; 

 
to ponieważ kod litery b (98) jest większy od kodu litery B (42), więc nastąpi wyprowadzenie napisu 
 

 

Pierwszy ła

ń

cuch jest mniejszy 

 

background image

 

39 

 
_________________________________________________________________________________________ 

Instrukcje  

 
Instrukcja  jest  opisem  czynności.  Każda  instrukcja  może  być  poprzedzona  etykietą  (albo  więcej  niż  jedną 
etykietą). Etykieta ma postać identyfikatora, a od instrukcji (albo od etykiety) oddziela ją znak : (dwukropek). 
 
Na przykład 
 

void sub(int par) 

    Lab:                    // etykieta 
    while(true) 
        if(par++ < 0) 
            break Lab;      // odwołanie do etykiety 

 
 

Instrukcja deklaracyjna 

 
Instrukcja deklaracyjna ma postać 
  

 

spec dcldcl, ... , dcl

 
w  której  spec  jest  zestawem  specyfikatorów,  a  każde  dcl  jest  deklaratorem  zmiennej  albo  deklaratorem  z 
inicjatorem. 
 
 

Specyfikatory 

Specyfikatory  służą  do  określenia  typu  zmiennych  i  rezultatów  procedur,  a  ponadto  określają  dodatkowe 
atrybuty zmiennychprocedur klas, takie jak 
 
 

dostępność 

privateprotectedpublic 

 

statyczność 

static 

 

rodzimość 

native 

 

ustaloność 

final 

 

synchroniczność 

synchronized 

 

ulotność 

volatile 

 

nietrwałość 

transient 

 
Na przykład 
 

public final 
class Master { 
    public static final double Pi = 3.14; 
    // ... 

 
Klasa Master jest publiczna (dostępna z wszystkich klas) i ustalona (nie może być podklasą innej klasy). 
 
Zmienna  Pi  jest  publiczna  (dostępna  z  wszystkich  klas),  statyczna  (istnieje  od  chwili  załadowania  do  chwili 
wyładowania klasy) i ustalona (nie mo
że być modyfikowana). 
 
 

Deklaratory 

Deklarator  służy  do  określenia  nazwy  zmiennej.  Jeśli  zmienna  jest  odnośnikiem  do  tablicy,  to  deklarator 
zawiera informację o liczbie jej wymiarów
 
Uwaga:  Sposób  rozmieszczenia  par  nawiasów  kwadratowych  określających  liczbę  wymiarów  tablicy  jest 
dowolny. W obrębie nawiasów kwadratowych nie może wystąpić żaden napis. 

background image

 

40 

 
Na przykład 
 

int var; 
int vec[]; 
String[] arr[];   // String arr[][];   String [][]arr; 
int mtx[2][]      // bł

ą

d (liczba w obr

ę

bie nawiasów) 

 
Deklaratorami są varvec[] i []arr[]. Identyfikator vec jest nazwą zmiennej typu "int", a identyfikatory vec arr 
s
ą  nazwami  odnośników  do  tablic  zmiennych,  odpowiednio  1-wymiarowej  i  2-wymiarowej  (wynika  to  z  liczby 
par nawiasów kwadratowych). 
 
 

Inicjatory 

Inicjator deklaratora może mieć jedną z następujących postaci 
 

 

exp 

inicjator wyra

ż

eniowy 

 

= { phiphi, ... , phi 

inicjator klamrowy 

 

w których exp jest wyrażeniem a każde phi jest wyrażeniem, albo frazą inicjującą o postaci 
 

 

phiphi, ... , phi 

 
Opracowanie  instrukcji  deklaracyjnej  powoduje  zadeklarowanie  wymienionych  w  niej  zmiennych  oraz 
ewentualne zainicjowanie ich wartościami wyrażeń wymienionych w inicjatorach. 
 
Uwaga: Inicjator klamrowy może być użyty tylko do inicjowania tablic. 
 
Przykład Instrukcje deklaracyjne 
 

int varA; 
int varB = 10; 
int var1, var2 = 20, var3; 
int lotto[] = { 12, 45, 6, 32, 18, 5 }; 
String monolog[] = { "To", "be", "or", "not", "to", "be" }; 
int matrix[][] = {  
                     { 10, 20, 30, 40 }, 
                     { 20, 30, 40, 50 }, 
                     { 30, 40, 50, 60 } 
                 }; 
int var5(50);               // bł

ą

d (niewła

ś

ciwy inicjator) 

int varC = { 10 };          // bł

ą

d (niewła

ś

ciwy inicjator) 

 

 
Zmienna matrix jest odnośnikiem do 2-wymiarowej tablicy o 2 wierszach i 4 kolumnach. 
 
 

Zakres deklaracji 

 
W  punkcie  bezpośrednio  za  deklaratorem  zaczyna  się  zakres  deklaracji  zmiennej.  Kończy  się  on  w  miejscu 
zakończenia najwęższego bloku, w którym wystąpiła ta deklaracja. 
 
Uwaga: Blokiem jest fragment programu zawarty między parą odpowiadających sobie nawiasów klamrowych
 
Na przykład 
 

import java.io.IOException; 
 
public  
class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
        if(args.length > 0) { 

background image

 

41 

            String allArgs = ""; 
            for(int i = 0; i < args.length ; i++) { 
                System.out.println( 
                    "Argument #" + i + ": " + args[i] 
                ); 
                allArgs += args[i] + ' '; 
            } 
            System.out.println("All arguments: " + allArgs); 
         } else  
            System.out.println("Program was called " +  
                               "with no arguments!"); 
         System.in.read(); 
    } 

  
W programie występują 4  wzajemnie zawierające się bloki. 
 
Wykonanie programu powoduje wyszczególnienie jego argumentów, na przykład 
 

 

Argument #0: Ewa 

 

Argument #1: Jan 

 

All arguments: Ewa Jan 

 
albo wyprowadzenie napisu 
 

 

Program was called with no arguments! 

 
 

Kolidowanie deklaracji 

 
W  zakresie  deklaracji  zmiennej  nie  może  wystąpić  deklaracja  zmiennej  oznaczonej  takim  samym 
identyfikatorem. 
 
Przykład Kolidowanie deklaracji 
 

void sub(int age) 

    System.out.println(age); 
    { 
        int age = 12;        // bł

ą

d (kolizja z parametrem) 

        // ... 
        { 
            int age = 20;    // bł

ą

d (dodatkowa kolizja) 

            String name = "Bob"; 
            // ... 
        } 
        String name = "Tom";  
        // ... 
        for(int i = 0; i < 10 ; i++) 
            // ... 
        for(int i = 10; i > 0 ; i--) 
            // ... 
    } 

 
Odnośnik  name  identyfikujący  obiekt  zainicjowany  łańcuchem  "Bob"  nie  koliduje  z  odnośnikiem  name 
identyfikuj
ącym obiekt zainicjowany łańcuchem "Tom". 
 
Nie występuje kolizja między identyfikatorami występującymi w instrukcjach for. 
 
 

Instrukcja pusta 

 
Instrukcja pusta ma postać  
 

 

background image

 

42 

 
Jej wykonanie nie wywołuje żadnych skutków. 
 
Przykład Instrukcja pusta 
 

void fun() 

    int i = 10000; 
    JustLabel: 
        ;                  // instrukcja pusta 
    while(i-- != 0) 
        ;                  // instrukcja pusta 
    Fin:                   // bł

ą

d (brak instrukcji pustej) 

 
 

Instrukcja grupująca 

 
Instrukcja grupująca ma postać 
 

 

Ins Ins ... Ins } 

 

w której każde Ins jest dowolną instrukcją. 
 
Wykonanie instrukcji grupującej składa się z sekwencyjnego wykonania zawartych w niej instrukcji Ins
 
Uwaga: Użycie instrukcji grupującej ma na celu utworzenie z sekwencji instrukcji dokładnie jednej instrukcji.  
 
Przykład Instrukcje grupujące 
 

int i = -100; 
do { 
    System.out.print(i++); 
    i++; 
} while(i < 0); 
 
do 
    System.out.print(i++); 
    i++;                // bł

ą

d (brak instrukcji grupuj

ą

cej) 

while(i < 100); 
     

Między pierwszą parą słów kluczowych do while występuje instrukcja grupująca. 
 
 

Instrukcja wyrażeniowa 

 
Instrukcja wyrażeniowa ma postać 
 
 

exp

 
w której exp jest wyrażeniem. 
 
Wykonanie  instrukcji  wyrażeniowej  składa  się  z  opracowania  wyrażenia  exp,  a  następnie  zignorowania 
rezultatu tego opracowania. 
 
Uwaga:  Wykonanie  instrukcji  wyrażeniowej  powinno  pociągać  za  sobą  skutki  uboczne,  takie  jak 
przypisywanie i przesyłanie danych. W przeciwnym razie użycie jej jest zbyteczne
 
Przykłady Instrukcje wyrażeniowe 
 

int var; 
// ... 
var = 10;                      // przypisanie 

background image

 

43 

var1 = var2 = 10;              // przypisanie 
var = System.in.read();        // przesłanie 
System.in.read();              // przesłanie 
System.out.println(var);       // przesłanie 

 
 

Instrukcja warunkowa 

 
Instrukcja warunkowa ma postać 
 

 

if(expInsT 

albo 

 

if(expInsT else InsF 

 
w których exp jest wyrażeniem orzecznikowym, a InsT oraz InsF jest instrukcją. 
 
Jeśli InsT jest instrukcją warunkową, to obowiązuje zasada, że każdej frazie else odpowiada najbliższa z lewej, 
poprzedzająca ją fraza if.  
 
W szczególności, instrukcja 
 

 

if(exp1) if(exp2Ins1 else Ins2 

 
jest równoważna instrukcji 
 

 

if(exp1) { if(exp2Ins1 else Ins2 } 

 
a nie instrukcji 
 

 

if(exp1) { if(exp2Ins1 } else Ins2 

 
Wykonanie  instrukcji  warunkowej  zaczyna  się  od  wyznaczenia  wartości  wyrażenia  exp.  Jeśli  wyrażenie  ma 
wartość true, to jest wykonywana instrukcja InsT, a w przeciwnym razie instrukcja InsF (o ile występuje). Po 
wykonaniu tych czynności wykonanie instrukcji warunkowej uznaje się za zakończone. 
 
Przykład Instrukcje warunkowe 
 

if(var < 5 && var != 0) 
    System.out.println(var); 
 
if(flag || var >= 5) 
    var = 0; 
else 
    System.out.println(var); 

 

if(var >= 0) { 
    if(var <= 9) 
        System.out.println(var); 
} else  
    var = -1; 
     

Instrukcja grupująca została użyta po to, aby przed frazą else nie wystąpiła instrukcja grupująca bez frazy else
 
Gdyby pominięto nawiasy klamrowe, to rozpatrywana instrukcja przybrałaby postać 
 

if(var >= 0) 
    if(var <= 9) 
        System.out.println(var); 
    else  
        var = -1; 

 
równoważną 
 

if(var >= 0) { 

background image

 

44 

    if(var <= 9) 
        System.out.println(var); 
    else  
        var = -1; 

 
a zatem o istotnie różnej semantyce. 
 
 

Instrukcje pętli 

 
Instrukcje  pętli  umożliwiają  cykliczne  wykonywanie  objętych  nimi  instrukcji.  Instrukcja  pętli  powinna  być 
skonstruowana w taki sposób, aby istniała gwarancja zakończenia jej wykonywania. 
 
 

Instrukcja while 

 
Instrukcja while ma postać 
 

 

while(expIns 

 
w której exp jest wyrażeniem orzecznikowym, a Ins jest instrukcją. 
 
Wykonanie instrukcji while składa się z cyklicznego wykonywania następujących czynności 
 
 

1.  Opracowania i wyznaczenia wartości wyrażenia exp

 

2.  Jeśli wyrażenie expC ma wartość true, wykonania instrukcji Ins

 
Jeśli wyrażenie ma wartość false, to wykonanie instrukcji while uznaje się za zakończone. 
 
Uwaga: Jeśli pierwszą wartością exp jest false, to instrukcja Ins w ogóle nie będzie wykonana. 
 
Przykład Instrukcja while 
 

int val = 3; 
while(val-- != 0) 
    System.out.println(val);  // 2 1 0 

 
Opracowanie wyrażenia  
 

 

val-- != 0 

 
ma skutek uboczny w postaci zmniejszenia wartości zmiennej val.  
 
Dzięki temu, po wykonaniu trzech obrotów pętli, nastąpi zakończenie wykonywania instrukcji while. 
 
 

Instrukcja for 

 
Instrukcja for ma postać 
 

 

for(Ins0 expC ; expIIns 

 
w której Ins0 jest instrukcją wyrażeniową albo deklaracyjną, expC jest wyrażeniem orzecznikowym, expI jest 
wyrażeniem, a Ins jest instrukcją. 
 
Wykonanie  instrukcji  for  składa  się  z  jednokrotnego  wykonania  instrukcji  Ins0,  a  następnie  z  cyklicznego 
wykonywania następujących czynności 
 
 

1.  Opracowania i wyznaczenia wartości wyrażenia expC

background image

 

45 

 

2.  Jeśli  wyrażenie  expC  ma  wartość  true,  wykonania  instrukcji  Ins  oraz  instrukcji  wyrażeniowej 

utworzonej z wyrażenia expI

 
Jeśli wyrażenie expC ma wartość false, to wykonanie instrukcji for uznaje się za zakończone. 
 
Uwaga: Warto odnotować, że wykonanie instrukcji 
 

 

for(Ins0 expC ; expI

          Ins 

 
ma na ogół taki sam skutek jak wykonanie instrukcji 
 

 

Ins0 

 

while(expC) { 

           Ins 
           expI
       } 

 

 

Przykład Instrukcja for 
 

for(int var = 0; var < 3 ; var++) 
    System.out.println(var);      // 0 1 2 
int var;  
for(var = 3; var > 0 ; var--) 
    System.out.println(var);      // 3 2 1 

 
 

Instrukcja do 

 
Instrukcja do ma postać 
 

 

do 

 

    Ins 

 

while(exp); 

 
w której Ins jest instrukcją, a exp jest wyrażeniem orzecznikowym. 
 
Wykonanie instrukcji do składa się z cyklicznego wykonywania następujących czynności 
 
 

1.  Wykonania instrukcji Ins

 

2.  Opracowania i wyznaczenia wartości wyrażenia exp

 

3.  Jeśli wyrażenie exp ma wartość true, ponownego wykonania podanych czynności. 

 
Jeśli wyrażenie exp ma wartość false, to wykonanie instrukcji do uznaje się za zakończone. 
 
Uwaga: Instrukcja Ins jest wykonywana co najmniej jeden raz. 
 
Przykład Instrukcja do 
 

int val = 3; 
do 
    System.out.println(val);  // 3 2 1 0 
while(val-- != 0); 

 
Ponieważ badanie warunku odbywa się dopiero po wykonaniu instrukcji zawartej między do i while, więc liczba 
"obrotów" p
ętli do jest o 1 większa niż liczba obrotów podobnej do niej instrukcji while. 
 
 

Instrukcje zaniechania i kontynuowania 

 
We wnętrzu pętli mogą być użyte instrukcje  

background image

 

46 

 

 

break; 

oraz 

 

continue; 

 
Wykonanie  instrukcji  zaniechania  (break)  powoduje  zakończenie  wykonywania  pętli,  natomiast  wykonanie 
instrukcji  kontynuowania  (continue)  powoduje  wykonanie  takich  czynności,  jakby  zakończyło  się 
wykonywanie wszystkich instrukcji objętej pętlą (co spowoduje kontynuowanie wykonywania pętli). 
 
Przykład Instrukcje break i continue 
 

int sum = 0; 
for(int i = 1; i <= 9 ; i++) { 
    if(i < 3) { 
        sum--; 
        continue; 
    } else if(sum > 7) 
        break;  
    sum = sum + 2*i; 

System.out.println("Sum = " + sum); 
 

Zmienna sum przyjmuje kolejno wartości: -1, -2, 4, 12. 
 
Zako
ńczenie wykonywania pętli for następuje znacznie wcześniej niż wynikałoby z rozpatrzenia jej pierwszego 
wiersza. 
 
Wykonanie podanego wycinka programu powoduje wyprowadzenie napisu 
 

 

Sum = 12 

 
 

Użycie etykiet 

 
Instrukcje zaniechania i kontynuowania mogą mieć również postać 
 

 

break Lab

oraz 

 

continue Lab

 
w których Lab jest etykietą. 
 
W  takim  wypadku  wykonanie  instrukcji  zaniechania  powoduje  zakończenie  wykonywania  pętli  poprzedzonej 
podaną  etykietą,  a  wykonanie  instrukcji  kontynuowania  powoduje  kontynuowanie  wykonania  pętli 
poprzedzonej podaną etykietą. 
 
Przykład  Instrukcja zaniechania z etykietą 
 

int arr[][] = {  
                  { 1, 2, 3 }, 
                  { 4, 0, 5 }, 
                  { 1, 1, 0 } 
              }; 
int sum = 0; 
Loop: 
for(int i = 0; i < 3 ; i++) 
    for(int j = 0; j < 3 ; j++)  
        if(arr[i][j] == 0) 
            break Loop; 
        else 
            sum += arr[i][j]; 
System.out.println(sum); 

 
Pętla sumuje kolejne wiersze tablicy, ale kończy się w chwili napotkania elementu o wartości 0

background image

 

47 

 
Wykonanie programu powoduje wyprowadzenie liczby 10
 
Gdyby z instrukcji zaniechania usunięto etykietę, nastąpiłoby wyprowadzenie liczby 12
 
 

Instrukcja wyboru 

 
Instrukcja wyboru ma na ogół postać 
 

 

switch(exp0) { 

 

    Case Case ... Case Default 

 

 
w której każde Case jest frazą o postaci 
 

 

case expIns Ins ... break; 

 
Default jest frazą o postaci 
 

 

default: Ins Ins ... Ins 

 
W takim zapisie, exp0 jest wyrażeniem całkowitym, każde exp jest wyrażeniem stałym całkowitym (zazwyczaj 
literałem albo symbolem), a każde Ins jest instrukcją, albo jest napisem pustym. 
 
Na przykład 
 

void sub(int par) 
    throws IllegalArgumentException 

    switch(par) { 
        case 0: 
           System.out.print("0"); 
           break; 
        case -1: 
           System.out.print("-1"); 
           break; 
        case +1: 
           System.out.print("+1"); 
           break; 
        default: 
           System.out.print("Wrong value"); 
           throw new IllegalArgumentException(); 
    } 
    System.out.println(); 

 

Wykonanie instrukcji  wyboru zaczyna się od opracowania  i wyznaczenia wartości wyrażenia exp0. Następnie 
wartość tego wyrażenia jest porównywana z wartościami wyrażeń exp zawartych w kolejnych frazach Case, aż 
do stwierdzenia równości.  
 
W takim wypadku są wykonywane instrukcje danej frazy. W przeciwnym razie są wykonywane instrukcje frazy 
Default  (domniemana  fraza  Default  składa  się  z  jednej  instrukcji  break;).  Po  zakończeniu  tych  czynności 
wykonanie instrukcji wyboru uznaje się za zakończone. 
 
Uwaga:  Jeśli  frazy  Case  nie  kończy  instrukcja  break;,  to  bezpośrednio  po  wykonaniu  jej  instrukcji  Ins  są 
wykonywane  instrukcje  następnych  fraz,  aż  do  napotkania  instrukcji  zaniechania  (break)  bądź  powrotu 
(return), albo do końca instrukcji wyboru. 
 
 

Instrukcje powrotu 

 
Instrukcja powrotu ma postać 

background image

 

48 

 

 

return; 

albo 

 

return exp

 
w której exp jest wyrażeniem. 
 
Wykonanie instrukcji powrotu powoduje zakończenie wykonywania zawierającej ją procedury. Jeśli procedura 
jest  rezultatowa  (jest  typu  różnego  od  "void"),  to  jej  rezultat  jest  inicjowany  wartością  wyrażenia  exp  (po 
ewentualnej konwersji do typu rezultatu). 
 
Przykład Instrukcja powrotu 
 

static double fun(int par) 

    if(par > 0) 
        return par * par;  // return (double)(par * par); 
    else 
        return 0.0; 

 
Jeśli funkcja fun zostanie wywołana w instrukcji 
 

System.out.print(fun(2)); 

 
to wartością jej rezultatu będzie 4.0
 
 

Instrukcja obsługi wyjątków 

 
Instrukcja do obsługi wyjątków ma postać 
 

 

try 

 

    Block 

 

Catch 

 

Finally 

 
w której Block jest instrukcją grupującą, Catch jest niepustym zestawem fraz 
 

 

catch(Dcl

 

    Block 

 
w  których  Dcl  jest  deklaracją  parametru  anonimowej  funkcji  do  obsługiwania  wyjątków,  a  Finally  jest 
nieobowiązkową frazą 
 

 

finally 

 

    Block 

 
Na przykład 
 

int len = 3, vec[]; 
try { 
    vec = new int [len]; 
    // ... 

catch(OutOfMemoryError e) { 
    // ... 

catch(Exception e) { 
    // ... 

finally { 
    vec = null; 
    // ... 

background image

 

49 

 
Wykonanie instrukcji try składa się z wykonania instrukcji grupującej występującej bezpośrednio po frazie try
Jeśli  podczas  jej  wykonywania  wystąpi  sytuacja  wyjątkowa  (spowodowana  na  przykład  brakiem  pamięci  na 
stercie), to wykonanie instrukcji grupującej uzna się za zakończone, a wysłany wówczas wyjątek (obiekt klasy 
wyjątku)  zostanie  odebrany  i  obsłużony  przez  tę  pierwszą  frazę  catch,  której  parametr  można  skojarzyć  z 
wysłanym wyjątkiem. 
 
Niezależnie od tego jaki był przebieg wykonania instrukcji try, ale bezpośrednio przed jej zakończeniem, jest 
wykonywany blok frazy finally
 
Jeśli  żadna  z  fraz  catch  instrukcji  try  nie  jest  w  stanie  odebrać  wyjątku,  to  jest  on  wysyłany  do  najwęższej 
dynamicznie
,  obejmującej  ją,  instrukcji  try.  Jeśli  takiej  nie  ma,  to  domyślnie  następuje  zakończenie 
wykonywania programu. 
 
Uwaga: Jeśli wystąpienie sytuacji wyjątkowej spowoduje zaniechanie dalszego wykonywania jakiegokolwiek 
bloku  programu,  to  nastąpi  niejawne  zniszczenie  wszystkich  jeszcze  nie  zniszczonych  jego  zmiennych 
lokalnych
 
Na przykład 
 

import java.io.IOException; 
 
public  
class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
        String str = fun("Hello"); 
        // ... 
        System.in.read(); 
    } 
    static String fun(String str) 
    { 
        try { 
            int fix = 12; 
            // ... 
            return getStr(str); 
        } 
        catch(OutOfMemoryError e) { 
            System.out.println("Buy more RAM!"); 
            System.exit(0); 
            return null;          // wymagane! 
        } 
    } 
    static String getStr(String str) 
        throws OutOfMemoryError 
    { 
        String strRef; 
        try { 
            strRef = new String(str); 
            System.out.println(strRef.charAt(0)); 
        }  
        catch(NullPointerException e) { 
            System.exit(1); 
            return null; 
        } 
        return strRef; 
    } 
}     

 
Jeśli podczas wykonywania instrukcji 
 

strRef = new String(str); 

 
zostałby wysłany wyjątek klasy OutOfMemoryError, to ponieważ nie mógłby zostać odebrany przez frazę  
 

background image

 

50 

catch(NullPointerException e) 

 
wchodzącą w skład instrukcji try funkcji getStr, więc zostałby wysłany do dynamicznie obejmującej ją instrukcji 
try funkcji fun, gdzie zostałby przechwycony przez fraz
ę  
 

catch(OutOfMemoryError e) 

 
Należy  zwrócić  uwagę,  że  tuż  po  rozpatrzeniu  fraz  catch  instrukcji  try  należącej  do  funkcji  getStr  zostanie 
zniszczona zmienna strRef, a tu
ż przed rozpatrzeniem fraz catch instrukcji try należącej do funkcji fun zostanie 
zniszczona zmienna fix
 
 
________________________________________________________________________________________ 

Tablice 

 
Tablica  jest  wektorem  elementów,  z  których  każdy  jest  takiego  samego  typu.  Elementami  tablicy  mogą  być 
tylko zmienne typu podstawowego oraz odnośniki do tablic i obiektów. 
 
Z  każdym  elementem  tablicy  jest  związany  indeks,  określający  pozycję  elementu  w  obrębie  tablicy.  Indeks 
pierwszego elementu ma wartość 0
 
Przetwarzanie elementów tablicy odbywa się za pośrednictwem odnośnika identyfikującego tablicę. Jeśli nazwą 
odnośnika jest ref, to wyrażenie  
 

 

ref[exp

 
w  którym  exp  jest  wyrażeniem  całkowitym,  jest  nazwą  elementu  o  indeksie  równym  wartości  wyrażenia  exp
zaś wyrażenie  
 

 

ref.length 

 
jest nazwą zmiennej o wartości równej liczbie elementów tablicy. 
 
 

Elementy podstawowe 

 
Jeśli Type jest nazwą typu podstawowego (np. "int" albo "double"), to deklaracja 
 

 

Type ref[] 

 
oznajmia,  że  ref  jest  odnośnikiem  do  wektora  elementów  typu  podstawowego,  z  których  każdy  jest  typu 
"Type". 
 
Podczas  opracowania  takiej  deklaracji,  odnośnikowi  ref  jest  przypisywane  odniesienie  puste.  Odniesienie 
niepuste można przypisać na dwa sposoby: albo za pomocą takiego inicjatora jak 
 

 

= { 10, 20, 30 } 

 
określającego wartości elementów, na przykład 
 

int vec[] = { 10, 20, 30 }; 
 

albo za pomocą wyrażenia fabrykującego  
 

 

new Type [size

 
dostarczającego odnośnik do size-elementowego wektora, którego elementy mają wartości 0, na przykład 
 

new int [width * height] 

 

background image

 

51 

 
A zatem, wykonanie następującej instrukcji 
 

int arr[] = { 10, 20, 30 }; 

 
może być zastąpione wykonaniem instrukcji 
 

int arr[]; 
arr = new int [3]; 
for(int i = 0; i < arr.length ; i++) 
    arr[i] = 10 * (i+1); 

 
Przykład Sumowanie elementów 
 

import java.io.IOException; 
 
public 
class Master { 
    public static void main(String args[]) 
        throws IOException 
    {   
        int vec[] = { 10, 20, 30 }; 
        int sum = 0; 
        for(int i = 0; i < vec.length ; i++) 
            sum = sum + vec[i]; 
        System.out.println("Sum = " + sum); 
        System.in.read(); 
    } 

 

Wykonanie programu powoduje wyprowadzenie napisu 
 

 

Sum = 60 

 

 
 

Elementy odnośnikowe 

 
Jeśli Type jest nazwą typu odnośnikowego (np. "String" albo "StringBuffer"), to deklaracja 
 

 

Type ref[] 

 
oznajmia, że ref jest odnośnikiem do wektora odnośników, z których każdy jest typu "Type". 
 
Podczas  opracowania  takiej  deklaracji,  odnośnikowi  ref  jest  przypisywane  odniesienie  puste.  Odniesienie 
niepuste można przypisać na dwa sposoby: albo za pomocą takiego inicjatora klamrowego jak 
 

 

= { "Hello", "World" } 

 
określającego wartości elementów, na przykład 
 

String vec[] = { "Hello", "World" }; 
 

albo za pomocą wyrażenia fabrykującego  
 

 

new Type [size

 
dostarczającego odnośnik do size-elementowego wektora, którego elementy mają wartości null, na przykład 
 

new String [rows * cols] 

 
 
A zatem, wykonanie następującej instrukcji 
 

String arr[] = { "Hello", "World" }; 

background image

 

52 

 
może być zastąpione wykonaniem instrukcji 
 

String arr[]; 
arr = new String [2]; 
arr[0] = "Hello"; 
arr[1] = "World"; 

 
Przykład Wyznaczenie średniej argumentów 
 

import java.io.IOException; 
 
public 
class Master { 
    public static void main(String args[]) 
        throws IOException, NumberFormatException 
    {   
        if(args.length == 0) 
            System.out.println("Please supply arguments"); 
            System.in.read(); 
            return; 
        } 
        int sum = 0, count = args.length; 
        for(int i = 0; i < count ; i++) { 
            String arg = args[i]; 
            int val = Integer.parseInt(arg); 
            sum = sum + val; 
        } 
        System.out.println(sum / count); 
        System.in.read(); 
        System.exit(0); 
    } 

 
Jeśli program Master zostanie wywołany za pomocą polecenia 
 

 

Master 10 20 30 

 
to wykonanie programu spowoduje wyprowadzenie liczby 20
 
Wykonanie instrukcji 
 

String arg = args[i]; 

 
powoduje  przypisanie  odnośnikowi  arg  odniesienia  do  obiektu  łańcuchowego  zainicjowanego  kolejnym 
argumentem programu. 
 
Wykonanie instrukcji 
 

int val = Integer.parseInt(arg); 

 
powoduje przekształcenie obiektu łańcuchowego w zmienną o wartości reprezentowanej przez łańcuch. 
 
Ponieważ wymienione przekształcenie może spowodować wysłanie wyjątku 
 

 

NumberFormatException 

 
więc w nagłówku funkcji main, występuje wymagana fraza throws
 
 

Reprezentowanie tablic 

 

background image

 

53 

Na  rysunku  Reprezentowanie  tablic  pokazano  sposób  przechowywania  w  pamięci  operacyjnej  tablic  jedno-  i 
dwuwymiarowych  o  elementach  typu  podstawowego  oraz  tablic  o  elementach  typu  obiektowego  "String", 
zadeklarowanych i zainicjowanych za pomocą następujących instrukcji. 
 

int vecI[]      =   { 10, 20, 30 }; 
 
int arrI[][]    = { { 10, 20, 30 },  
                    { 40, 50, 60 } }; 
 
String vecS[]   =   { "Java", "is", "cool" }; 
 
String arrS[][] = { { "Java", "is", "much" },  
                    { "cooler", "than", "C++" } }; 

background image

 

54 

 
Rysunek Reprezentowanie tablic 

### 

 

 

 

 

 

 

 

vecI 

 

 

 

 

 

 

 

 

 

 

 

 

10 

 

 

 

 

 

 

20 

 

 

 

 

 

 

30 

arrI 

 

 

 

 

 

 

 

 

 

arrI[0] 

 

 

10 

 

 

 

 

 

 

20 

 

 

 

 

 

 

30 

 

 

 

arrI[1] 

 

 

40 

 

 

 

 

 

 

50 

 

 

 

 

 

 

60 

vecS 

 

 

 

 

 

 

 

 

 

vecS[0] 

 

 

Java 

 

 

 

vecS[1] 

 

 

is 

 

 

 

vecS[2] 

 

 

cool 

arrS 

 

 

 

 

 

 

 

 

 

arrS[0] 

 

arrS[0][0] 

Java 

 

 

 

 

 

arrS[0][1] 

is 

 

 

 

 

 

arrS[0][2] 

much 

 

 

 

arrS[1] 

 

arrS[1][0] 

cooler 

 

 

 

 

 

arrS[1][1] 

than 

 

 

 

 

 

arrS[1][2] 

C++ 

 

 

 

 

 

 

 

### 

 
 
_________________________________________________________________________________________ 

Obiekty 

 
Obiektem  jest  zmienna  strukturalna  stanowiąca  egzemplarz  klasy.  Podczas  definiowania  klasy  wyszczególnia 
się składniki jej obiektów: polakonstruktory metody
 
Pole jest składnikiem, który określa właściwości zmiennej wchodzącej w skład obiektu. 
 
Konstruktor jest składnikiem, który określa sposób inicjowania obiektu klasy. 
 
Metoda jest składnikiem, który określa operację jaką można wykonać na obiekcie klasy. 
 
Uwaga: Definicja klasy może ponadto zawierać definicje funkcji zmiennych statycznych. Zmienne i funkcje 
statyczne nie wchodzą w skład poszczególnych obiektów klasy, ale są wspólne wszystkim jej obiektom.  
 
Przykład Klasa Cplx 
 

class Cplx { 
    double re = 0,                 // pole 
           im = 0;                 // pole 
    static int count = 0;          // zmienna 
    Cplx(int rePar, double imPar)  // konstruktor 
    { 
        re = rePar; 
        im = imPar; 
        count++; 
    } 
    void outCplx()                 // metoda 
    { 

background image

 

55 

        System.out.println("(" + re + "," + im + ")"); 
    } 
    static void outCount()         // funkcja 
    { 
        System.out.println(count); 
    } 

 
Klasa Cplx opisuje rodzinę obiektów reprezentujących liczby zespolone o części rzeczywistej re i urojonej im
 
W  każdym  obiekcie  klasy  Cplx  występują  zmienne  opisane  przez  pola  re  i  im  oraz  jeden  konstruktor  i  jedna 
metoda. 
 
Zmienna count oraz funkcja outCount jest wspólna wszystkim obiektom klasy Cplx. Składniki te występują poza 
wszystkimi obiektami klasy. 
 
 

Tworzenie obiektów 

 
Utworzenie obiektu odbywa się za pomocą wyrażenia fabrykującego 
 

 

new Class(argargarg

 
w  którym  Class  jest  nazwą  klasy  obiektu,  a  każde  arg  jest  argumentem  kojarzonym  z  odpowiadającym  mu 
parametrem konstruktora obiektu. 
 
Bezpośrednio  po  utworzeniu  obiektu  jest  wywoływany  konstruktor  klasy  Class.  W  jego  ciele  jest  dostępny 
odnośnik  this,  identyfikujący  właśnie  utworzony  obiekt.  Rezultatem  operacji  new  jest  odnośnik  zainicjowany 
kopią odnośnika this
 
Jeśli  w  klasie  obiektu  zdefiniowano  pole  field,  to  nazwą  zmiennej  wchodzącej  w  skład  sfabrykowanego 
obiektu,  a  opisanej  przez  to  pole,  jest  this.field.  Nazwę  tę  można  uprościć  do  field  tylko  wówczas,  gdy  w 
miejscu jej wystąpienia, deklaracja pola jest widoczna (nie przesłonięta przez inną deklarację). 
 
Uwaga: Jeśli klasa Class nie zawiera definicji ani jednego konstruktora, to jest niejawnie uzupełniana definicją 
konstruktora domyślnego 
 

 

public Class() 

 

 

    super(); 

 

 
 
Przykład Odnośnik this 
 

import java.io.IOException; 
 
class Cplx { 
    double re = 0, im = 0; 
    Cplx(int re, double im) 
    { 
        this.re = re; 
        this.im = im; 
    } 
    void out() 
    { 
        System.out.println("(" + re + "," + im + ")"); 
    } 

 
public 
class Master { 
    public static void main(String args[]) 
        throws IOException 

background image

 

56 

    { 
        Cplx ref = new Cplx(3, 4); 
        ref.out();         // (3,4) 
        System.in.read(); 
    } 

 
Opracowanie wyrażenia fabrykującego 
 

 

new Cplx(3, 4) 

 
powoduje  utworzenie  obiektu  składającego  się  z  2  zmiennych  typu  "double",  utworzenia  odnośnika  this 
zainicjowanego odniesieniem do obiektu, a nast
ępnie wywołania konstruktora klasy Cplx z argumentami 3 i 4
 
Ponieważ odnośnik this identyfikuje właśnie utworzony obiekt, więthis.re jest nazwą zmiennej reprezentującej 
jego cz
ęść rzeczywistą, a this.im jest nazwą zmiennej reprezentującej jego część urojoną
 
W  ciele  konstruktora  zmiennym  tym  są  przypisywane  dane  o  wartościach  3  i  4,  określonych  przez  argumenty 
konstruktora. 
 
Ponieważ  w  miejscu  wystąpienia  napisu  this.re  nie  jest  widoczna  deklaracja  pola  re  (gdyż  jest  przesłonięta 
przez deklaracj
ę parametru re), więc uproszczenie this.re do re nie jest dozwolone. 
 
 

Niszczenie obiektów 

 
Niszczenie  obiektów  jest  automatyczne.  Zniszczenie  obiektu  odbywa  się  nie  wcześniej,  niż  w  chwili,  gdy 
istnieje pewność, że ani jednemu odnośnikowi nie jest przypisane odniesienie do obiektu. 
 
Przykład Niszczenie obiektów 
 

import java.io.IOException; 
 
class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
        { 
            Cplx ref = new Cplx(1, 2); 
        } 
        Cplx ref = new Cplx(3, 4); 
        ref = new Cplx(5, 6); 
        ref.out();         // (5, 6) 
        ref = null; 
        ref.out();         // bł

ą

d (NullPointerException) 

        System.out.println("Done"); 
        System.in.read(); 
    } 

 
Ponieważ po wykonaniu instrukcji 
 


    Cplx ref = new Cplx(1, 2); 

 
nie  istnieje  już  odnośnik  identyfikujący  obiekt  Cplx(1,  2),  więc  w  dowolnej  chwili  obiekt  ten  może  być 
zniszczony. 
 
Ponieważ po wykonaniu instrukcji 
 

ref = new Cplx(5, 6); 

 

background image

 

57 

nie  istnieje  już  odnośnik  identyfikujący  obiekt  Cplx(3,  4),  więc  w  dowolnej  chwili  obiekt  ten  może  być 
zniszczony. 
 
Analogicznie, po wykonaniu instrukcji 
 

ref = null; 

 
może być zniszczony obiekt Cplx(5, 6)
 
W miejscu wykonania instrukcji 
 

ref.out(); 

 
zostanie  wysłany  wyjątek  klasy  NullPointerException.  Ponieważ  wyjątek  nie  jest  obsłużony  (brak  instrukcji 
try), wi
ęc nastąpi zakończenie wykonywania programu, bez wykonania instrukcji 
 

System.out.println("Done"); 

 
 

Fabrykowanie obiektów 

 
Obiekty  można  fabrykować  za  pomocą  operacji  new  oraz  za  pomocą  metod  fabrykujących,  jak  na  przykład 
newInstance.  Metoda  newInstance  jest  wywoływana  na  rzecz  obiektu  opisującego  klasę,  której  obiekt  ma 
zostać sfabrykowany. 
 
 

public Class getClass() 

Metoda zwraca odnośnik do obiektu opisującego klasę tego obiektu, na rzecz którego ją wywołano. 
 

public static Class forName(String className) 
    throws ClassNotFoundException 

Metoda zwraca odnośnik do obiektu opisującego klasę o nazwie identyfikowanej przez className
 

public Object newInstance()  
    throws InstantiationException,  
           IllegalAccessException 

Metoda  zwraca  odnośnik  do  obiektu  klasy  opisanej  przez  obiekt  klasy  Class  na  rzecz  którego  ją  wywołano. 
Sfabrykowany obiekt jest inicjowany przez konstruktor domyślny. 
 
 
Przykład Fabrykowanie obiektów 
 

import java.io.IOException; 
 
public 
class Master { 
    public static void main(String args[]) 
        throws IOException, 
               ClassNotFoundException, 
               InstantiationException, 
               IllegalAccessException 
    { 
        Class childObj = Class.forName("Child"); 
        Object child = childObj.newInstance(); 
        Child isa = (Child)child; 
        System.out.println(isa.getName() + " is " +  
                           isa.getAge()); 
        System.in.read(); 
    } 

 
class Child { 
    private String name = "Isabel"; 
    private int age; 

background image

 

58 

    public Child() 
    { 
        age = 13; 
    } 
    public String getName() 
    { 
        return name; 
    } 
    public int getAge() 
    { 
        return age; 
    } 

 
Wykonanie programu powoduje wyprowadzenie napisu  
 

 

Isabel 

 
 

Wywoływanie procedur 

 
Wywołaniami  procedur  są  wywołania  konstruktorów,  metod  i  funkcji.  Konstruktor  wywołuje  się  na  rzecz 
obiektu  utworzonego  podczas  opracowania  wyrażenia  fabrykującego  albo  z  wnętrza  innego  konstruktora,  na 
rzecz właśnie inicjowanego obiektu. Metodę wywołuje się na rzecz obiektu identyfikowanego przez odnośnik. 
Funkcję wywołuje się w oderwaniu od jakiegokolwiek obiektu. 
 
Jeśli w ciele klasy zdefiniowano więcej niż jedną procedurę o takiej samej nazwie, to jest wywoływana ta, do 
której parametrów najlepiej pasują podane argumenty wywołania.  
 
W  szczególności,  jeśli  klasa  zawiera  więcej  niż  jeden  konstruktor,  to  wywołuje  się  ten,  którego  liczba 
parametrów jest równa liczbie argumentów wywołania. 
 
Na przykład 
 

String isa = "Isabel"; 
System.out.print(isa.toUpperCase());  // ISABEL 

 
Metodę toUpperCase wywołano na rzecz obiektu identyfikowanego przez odnośnik isa
 
 

Inicjowanie parametrów i rezultatów 

 
W  ramach  wywołania  procedury  odbywa  się  kojarzenie  jej  parametrów  z  argumentami  wywołania.  Każde 
skojarzenie polega na zainicjowaniu parametru wartością kojarzonego z nim argumentu.  
 
Jeśli rezultat procedury jest typu różnego od "void", to w chwili wykonania instrukcji powrotu, inicjuje się go 
wartością wyrażenia podanego w tej instrukcji. 
 
Uwaga:  Parametr  jest  lokalną  zmienną  procedury,  a  zmiana  wartości  parametru  nie  ma  żadnego  wpływu  na 
wartość skojarzonego z nim argumentu. 
 
Przykład Inicjowanie parametrów i rezultatu 
 

import java.io.IOException; 
 
class Child { 
    String name; 
    int age = 0; 
    Child(String name, int age) 
    { 
        this.name = name; 
        this.age  = age; 
    } 

background image

 

59 

    Child(String name) 
    { 
        this.name = name; 
    } 
    void setAge(int age) 
    { 
        this.age = age; 
    } 
    int getAge() 
    { 
        return age; 
    } 
    void show() 
    { 
        if(age != 0) 
            System.out.println(name + " is " + age); 
        else 
            System.out.println(name + "! How old are you?"); 
    } 

 
public 
class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
        Child tom = new Child("Tom"); 
        tom.show(); 
        tom.setAge(12); 
        tom.show(); 
        Child isa = new Child("Isa", 13); 
        isa.show(); 
        System.in.read(); 
    } 

 
Podczas wykonania instrukcji 
 

tom.setAge(12); 

 
następuje  skojarzenie  parametru  age  metody  setAge  z  argumentem  12,  a  w  ramach  tej  czynności  niejawne 
wykonanie instrukcji
 
 

age = 12 

 
Podczas wykonania instrukcji 
 

return age; 

 
następuje skojarzenie rezultatu metody getAge ze zmienną reprezentowaną przez wyrażenie age, a w ramach tej 
czynno
ści niejawne wykonanie instrukcji 
 

res = age; 

 
w której res reprezentuje rezultat funkcji. 
 
Wykonanie programu powoduje wyprowadzenie napisu 
 

 

Tom! How old are you? 

 

Tom is 12 

 

Isa is 13 

 
 

Wywoływanie konstruktorów 

 
Konstruktor jest wywoływany podczas opracowywania wyrażenia fabrykującego  
 

background image

 

60 

 

new Class(argarg, ... , arg

 
w którym Class jest nazwa klasy, a każde arg jest argumentem wywołania. 
 
Ponadto,  ale  tylko  w  ciele  konstruktora  i  jako  jego  pierwsza  instrukcja  wyrażeniowa,  mogą  być  użyte 
wywołania 
 

 

this(argarg, ... , arg

 
oraz  
 

 

super(argarg, ... , arg

 
Pierwsze  z  nich  jest  wówczas  wywołaniem  innego  konstruktora  tej  samej  klasy,  a  drugie  wywołaniem 
konstruktora jej nadklasy. 
 
Przykład Wywoływanie konstruktorów 
 

import java.io.IOException; 
 
class Person { 
    String name; 
    int age; 
    Person(String name) 
    { 
        this.name = name; 
        age = -1;       // this.age = -1; 
    } 
    Person(String name, int age) 
    { 
        this.name = name; 
        this.age  = age; 
    } 

 
class Woman extends Person { 
    Person husband = null; 
    Woman(String name, int age) 
    {         
        super(name, age); 
    } 
    Woman(String name, int age, Person husband) 
    { 
        this(name, age); 
        this.husband = husband; 
    } 

         
class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
        Person john = new Person("John Smith", 40); 
        Woman  mary = new Woman("Mary Smith", 30, john); 
        System.in.read(); 
    } 
}    

 
Wykonanie instrukcji 
 

super(name, age); 

 
powoduje wywołanie dwuparametrowego konstruktora klasy Person
 
Wykonanie instrukcji 
 

this(name, age); 

background image

 

61 

 
powoduje wywołanie dwuparametrowego konstruktora klasy Woman
 
 

Wywoływanie metod 

 
Wywołanie metody ma postać  
 

 

ref.name(argarg, ... , arg

albo 

 

super.name(argarg, ... , arg

 
w  której  ref  jest  odnośnikiem  identyfikującym  obiekt,  name  jest  nazwą  metody  widocznej  w  klasie  obiektu, 
słowo kluczowe super określa, że chodzi o metodę nadklasy, a każde arg jest argumentem wywołania. 
 
Uwaga:  Jeśli  ref  jest  odnośnikiem  this,  a  w  miejscu  wywołania  jest  widoczna  definicja  metody  name,  to 
wywołanie można uprościć do 
 

 

name(argarg, ..., arg

 
Takie samo uproszczenie można zastosować do wywołania ze słowem kluczowym super
 
W  chwili  wywołania  metody  kojarzy  się  każdy  z  jej  parametrów  z  odpowiadającym  mu  argumentem,  a 
następnie tworzy odnośnik this i inicjuje go odniesieniem do obiektu na rzecz którego odbywa się wywołanie. 
Po wykonaniu tych czynności wykonuje się ciało metody name
 
Uwaga:  Jeśli  odnośnik  ref  jest  typu  "Class",  to  w  klasie  Class  musi  być  widoczna  metoda  name.  Podczas 
wykonania  programu  typ  odnośnika  jest  nieistotny:  wywołuje  się  metodę  name  widoczną  w  klasie  obiektu 
identyfikowanego przez odniesienie przypisane odnośnikowi ref
 
Przykład Wywoływanie metod 
 

import java.io.IOException; 
 
class Person { 
    String name; 
    int age; 
    Person(String name, int age) 
    { 
        this.name = name; 
        this.age  = age; 
    } 
    void setAge(int age) 
    { 
        System.out.println("In Person.setAge"); 
        this.age = age; 
    } 
    void showAge() 
    { 
        System.out.println(age); 
    } 

 
class Woman extends Person { 
    Person husband = null; 
    Woman(String name, int age, Person husband) 
    { 
        super(name, age); 
        this.husband = husband; 
    } 
    void setAge(int age) 
    { 
        System.out.println("In Woman.setAge"); 
        super.setAge(age); 
    } 

background image

 

62 


         
class Master { 
    public static void main(String args[]) 
        throws IOException 
    {      
        Woman  mary = new Woman("Mary Smith", 30, null); 
        Person ref  = mary; 
        ref.setAge(32); 
        ref.showAge();    // 32 
        System.in.read(); 
    } 
}    

 
Odnośnik mary jest typu "Woman". Przypisano mu odniesienie do obiektu klasy Woman
 
Odnośnik Person jest typu "Person". Przypisano mu odniesienie do obiektu klasy Woman
 
Ponieważ  wywołanie  metody  setAge  odbywa  się  na  rzecz  obiektu  mary  klasy  Woman,  a  nie  na  rzecz  obiektu 
klasy Person, wi
ęc wykonanie instrukcji 
 

ref.setAge(32); 

 
powoduje wywołanie metody setAge klasy Woman, a nie metody setAge klasy Person
 
Natomiast wykonanie w ciele metody Woman.setAge instrukcji 
 

super.setAge(age); 

 
powoduje wywołanie metody Person.setAge. 
 
Wykonanie programu powoduje wyprowadzenie napisu 
 

 

In Woman.setAge 

 

In Person.setage 

 

32 

 
 

Wywoływanie funkcji 

 
Wywołanie funkcji klasy Class ma postać 
 

 

Class.name(argarg, ..., arg

 
w  której  name  jest  nazwą  funkcji  widocznej  w  klasie  Class,  a  każde  arg  jest  argumentem  wywołania.  Jeśli 
wywołanie znajduje się w zakresie deklaracji funkcji name, to można je uprościć do 
 

 

name(argarg, ..., arg

 
W  chwili  wywołania  funkcji  kojarzy  się  każdy  z  jej  parametrów  z  odpowiadającym  mu  argumentem,  a 
następnie wykonuje ciało funkcji name
 
Dla  funkcji  nie  tworzy  się  odnośnika  this,  a  więc  w  każdym  zawartym  w  niej  odwołaniu  do  niestatycznego 
składnika klasy musi wystąpić jawny odnośnik.  
 
Uwaga:  Z  powodu  braku    odnośnika  this,  odwołanie  do  statycznego  składnika  klasy  jest  zazwyczaj 
kwalifikowane nazwą klasy. Jest to zbyteczne jeśli  w punkcie odwołania składnik jest widoczny. 
 
Przykład Wywoływanie funkcji 
 

import java.io.IOException; 
 
public 

background image

 

63 

class Master { 
    static void printLine(String line) 
    { 
        System.out.println(line); 
    } 
    void printStars(int count) 
    { 
        for(int i = 1; i <= count ; i++) 
            System.out.print("*"); 
        System.out.println(); 
    } 
    public static void main(String args[]) 
        throws IOException 
    { 
        String name = "John"; 
        Master.printLine(name);   // printLine(name) 
        Master refObj = new Master(); 
        refObj.printStars(3); 
        printLine(name);          // Master.printLine(name) 
        System.in.read(); 
    } 

 

Ponieważ wywołanie 
 

Master.printLine(name) 

 
znajduje się w zakresie deklaracji statycznej funkcji printLine, więc można je uprościć do 
 

printLine(name) 

 
Wywołanie 
 

refObj.printStars(3) 

 
nie może być uproszczone do 
 

printStars(3) 

 
ani zastąpione wywołaniem 
 

Master.printStars(3) 

 
ponieważ składnik printStars nie jest statyczny. 
 
Wykonanie programu powoduje wyprowadzenie napisu 
 

 

John 

 

*** 

 

John 

 

 

Wywołanie rekurencyjne 

 
Wywołanie  metody  i  funkcji  może  być  rekurencyjne.  Rekurencja  polega  na  tym,  że  jeszcze  przed 
zakończeniem wykonania pewnej procedury, jest ona wywoływana ponownie. Taki sposób postępowania może 
znakomicie  uprościć  zakodowanie  procedury,  jednak  w  pewnych  warunkach  może  spowodować  wydłużenie 
czasu jej wykonywania lub zwiększenie ilości wymaganych przez nią zasobów. 
 
Przykład Wyznaczenie potęgi  
 
Rozwiązanie iteracyjne 
 

int powerOf2(int pow) 

background image

 

64 

    int power = 1; 
    while(pow-- != 0) 
        power += power; 
    return power; 

 
 
Rozwiązanie rekurencyjne 
 

int powerOf2(int pow) 

    if(pow == 0) 
        return 1; 
    else { 
        int halfPower = powerOf2(pow-1); 
        return halfPower + halfPower; 
    } 

 
 

Przetwarzanie obiektów 

 
Przetwarzanie  obiektów  polega  na  ich  tworzeniu  oraz  na  wywoływaniu  metod  na  rzecz  obiektów. 
Przetwarzanie obiektów za pomocą funkcji wymaga dostarczenia im odnośników do obiektów. W ciele funkcji 
taki odnośnik może być użyty do wywołania metody na rzecz obiektu. 
 
Następujący  program  ilustruje  zasadę  utworzenia  stosu.  Elementami  stosu  są  obiekty  klasy  Item  zawierające 
zmienne typu "int".  
 
Uwaga:  Stosem  jest  kolekcja,  do  której  można  dokładać  elementy,  ale  z  której  można  je  wyjmować  tylko  w 
kolejności odwrotnej do dokładania. 
 
Przykład Projektowanie stosu 
 

import java.io.IOException; 
 
class Item { 
    Item refNext = null; 
    int value; 
    Item(int val) 
    { 
        value = val; 
    } 

 
class Stack { 
    Item refHead = null; 
    void push(int val) 
    { 
        Item refNew = new Item(val); 
        refNew.refNext = refHead; 
        refHead = refNew; 
    } 
    int pop() 
    { 
        if(refHead == null) 
            throw new RuntimeException(); 
        else { 
            int res = refHead.value; 
            refHead = refHead.refNext; 
            return res; 
        } 
    } 

 
public 
class Master { 
    public static void main(String args[]) 

background image

 

65 

        throws IOException 
    { 
        int vec[] = { 10, 20, 30, 40 }; 
        Stack stackA = new Stack(),  
              stackB = new Stack(); 
        for(int i = 0; i < vec.length ; i++)  
            stackA.push(vec[i]); 
        for(int i = 0; i < vec.length ; i++) { 
            int val = stackA.pop(); 
            System.out.print(val + " "); 
            stackB.push(val); 
        } 
        System.out.println(); 
        for(int i = 0; i < vec.length ; i++)  
            System.out.print(stackB.pop() + " "); 
        System.out.println(); 
        System.in.read(); 
    } 

 

Wykonanie programu powoduje wyprowadzenie napisu 
 

 

40 30 20 10 

 

10 20 30 40 

 
 

Przetwarzanie klas 

 
Warunkiem  utworzenia  obiektu  jest  istnienie  definicji  jego  klasy.  Definicje  klas  są  ładowane  dynamicznie  w 
chwili  gdy  po  raz  pierwszy  zaistnieje  potrzeba  utworzenia  obiektu.  Ponieważ  ładowanie  może  odbywać  się  z 
sieci rozległej, co jest obarczone ryzykiem załadowania klasy naruszającej bezpieczeństwo systemu ładującego, 
stosuje się szereg zabezpieczeń eliminujących to zagrożenie. 
 
W celu wdrożenia zabezpieczeń ładowanie klas odbywa się pod nadzorem zarządcy ładowania (Class Loader), 
a wykonywanie B-kodu pod nadzorem zarządcy ochrony (Security Manager).  
 
Zarządca  ładowania  dokonuje  weryfikacji  klasy  oraz  sprawdza  poprawność  jej  powiązań  z  uprzednio 
załadowanymi klasami.  
 
Zarządca ochrony przeprowadza dynamiczną kontrolę uprawnień programu do wykonywania niebezpiecznych 
dla Systemu czynności (np. apletowi zezwala na wykonywanie operacji wejścia-wyjścia tylko w odniesieniu do 
komputera z którego załadowano jego klasę).  
 
Oba te mechanizmy łącznie uniemożliwiają wprowadzenie do systemu programów, które mogłyby wymknąć się 
spod jego kontroli. 
 
Uwaga:  Zainstalowanie  zarządcy  ochrony  jest  wykonywane  jednokrotnie  dla  danej  aplikacji.  Jeśli  jest  nią 
przeglądarka (np. Netscape), to nie może być zmienione przez wrogi aplet ładowany podczas nawigowania po 
stronach WWW
 
 

Weryfikacja 

 
Weryfikacja  polega  na  sprawdzeniu,  czy  B-kod  klasy  jest  właściwie  uformowany,  czy  zawiera  odpowiednią 
tablicę symboli oraz czy spełnia wymagania syntaktyczne określone w specyfikacji języka, w tym czy zawiera 
wyłącznie  dopuszczalne  kody  operacji,  przeniesienia  sterowania  tylko  do  początku  instrukcji  oraz  właściwe 
sygnatury procedur. 
 
 

Przygotowanie 

 

background image

 

66 

Przygotowanie  polega  na  przydzieleniu  pamięci  dla  zmiennych  statycznych  oraz  na  przypisaniu  im  wartości 
domyślnych (np. odnośnikom wartości null, a zmiennym arytmetycznym wartości 0). 
 
 

Związanie 

 
Związanie  polega  na  zastąpieniu  odwołań  symbolicznych  do  klas,  interfejsów,  zmiennych  i  pól  odwołaniami 
bezpośrednimi,  wykorzystującymi  adresy  Maszyny  Wirtualnej.  Podczas  związywania  bada  się  również,  czy 
załadowana  klasa  zawiera  wszystkie  wymagane  składniki  oraz  czy  są  respektowane  prawa  dostępu  do 
składników. 
 
 

Zainicjowanie 

 
Zainicjowanie  składa  się  z  przypisania wartości początkowych zmiennym  statycznym  klasy  oraz z wykonania 
kodu jej inicjatorów
 
Uwaga:  Zainicjowanie  klasy  jest  wykonywane  dopiero  po  zweryfikowaniu  i  załadowaniu  nadklasy  danej 
klasy. Opracowywanie inicjatorów zmiennych statycznych oraz inicjatorów klasy odbywa się w kolejności ich 
wystąpienia w klasie. 
 
 

Zmienne statyczne 

Zmienną  statyczną  jest  zmienna  zadeklarowana  ze  specyfikatorem  static.  Taka  zmienna  nie  jest  elementem 
obiektu,  ale  jest  wspólna  wszystkim  obiektom  klasy.  Istnieje  nawet  wówczas  gdy  nie  utworzono  ani  jednego 
obiektu jej klasy. 
 
Na przykład 
 

class Master { 
    static int var = 12; 
    // ... 

 
Zmienna statyczna var jest inicjowana podczas inicjowania klasy. 
 
 

Inicjatory klasy 

Inicjatorem klasy jest fraza 
 

 

static Block 

 
w której Block jest instrukcją grupującą.  
 
Opracowanie inicjatora składa się z wykonania zawartej w niej instrukcji grupującej. 
 
Na przykład 
 

import java.io.IOException; 
 
class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
        System.in.read(); 
    } 
    static int var; 
    static { 
        var = 12; 
        System.out.println("Variable var initialized"); 
    } 
    // ... 

background image

 

67 

    static { 
        var++; 
        System.out.println("Variable var reinitialized"); 
    } 

 
Wykonanie programu powoduje wyprowadzenie napisu 
 

 

Variable var initialized 

 

Variable var reinitialized 

 
 

Aktywne użycie 

 
Klasa,  której  załadowanie  jest  niezbędne  do  dalszego  wykonania  programu  jest  uznawana  za  aktywnie  użytą. 
Strategia  ograniczenia  się  do  ładowania  klas  aktywnie  użytych,  dobrze  nadaje  się  do  programowania  w 
Internecie,  ponieważ  wyklucza  ładowanie  klas,  które  podczas  pewnych  wykonań  programu  są  dla  niego 
całkowicie zbędne.  
 
Uwaga:  Za  poprawną  uznaje  się  implementację,  w  której  wszystkie  związania  klas,  są  dokonywane  jeszcze 
przed podjęciem wykonywania programu.  
 
Przykład Aktywne użycie 
 

import java.io.IOException; 
 
class Point { 
    int x, y; 
    static int count = 0; 
    static { 
        System.out.println("Point initialized"); 
    } 
    Point() 
    { 
        x = y = -1; 
    } 

 
class Point3d extends Point { 
    static { 
        System.out.println("Point3d initialized"); 
    } 
    int z; 

 
public 
class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
        Point3d spacePoint; 
        spacePoint = new Point3d(); 
        // ... 
        System.in.read(); 
    } 

 
 
Wykonanie instrukcji 
 

Point3d spacePoint; 

 
powoduje utworzenie zmiennej spacePoint, ale nie wymaga  załadowania klasy Point3d, ponieważ nie istnieje 
jeszcze potrzeba utworzenia obiektu tej klasy. 
 
Opracowanie operacji 

background image

 

68 

 

new Point3d() 

 
wymaga załadowania klasy Point3d.  
 
W  ramach  ładowania  klasy  Point3d  jest  wykonywana  jej  weryfikacja,  przygotowanie  i  związanie.  Tuż  przed 
zainicjowaniem klasy jest ładowana jej nadklasa Point
 
W  ramach  ładowania  klasy  Point  są  wykonywane  identyczne  czynności  jak  dla  klasy  Point3D,  ale  przed  jej 
zainicjowaniem jest ładowana i inicjowana jej nadklasa Object
 
Po  załadowaniu  i  zainicjowaniu  klasy  Object  jest  inicjowana  klasa  Point.  Powoduje  to  przypisanie  zmiennej 
var warto
ści początkowej 0 oraz wykonanie inicjatora 
 

static { 
        System.out.println("Point initialized"); 

 
W analogiczny sposób jest inicjowana klasa Point3D
 
Wykonanie programu powoduje wyprowadzenie napisu 
 

 

Point initialized 

 

Point3d initialized 

 
 
_________________________________________________________________________________________ 

Strumienie 

 
Strumieniem wejściowym jest sekwencja danych pochodząca z pliku, z pamięci operacyjnej, albo z urządzenia
Dane  mają  zazwyczaj  postać  sekwencji  bajtów,  ale  za  pomocą  metod  takich  predefiniowanych  klas 
wejściowych jak StreamTokenizer StringTokenizer mogą być interpretowane jako ciągi leksemów.  
 
Strumieniem  wyjściowym  jest  sekwencja  danych  wysyłana  do  pliku,  do  pamięci  operacyjnej,  albo  do 
urządzenia.  Wysyłane  dane  mają  postać  sekwencji  bajtów,  ale  za  pomocą  predefiniowanej  klasy  wyjściowej 
PrintStream mogą być przekształcane w ciągi znaków. 
 
 

Klasa FileInputStream 

 
Klasa  FileInputStream  umożliwia  wykonywanie  operacji  wprowadzania  danych  na  obiektach 
reprezentujących strumienie wejściowe.  
 
Wykonanie operacji 
 

 

new FileInputStream(fileName

 
w  której  fileName  jest  wyrażeniem  typu  "String"  określającym  nazwę  pliku,  powoduje  utworzenie  obiektu 
reprezentującego strumień związany z plikiem o podanej nazwie. 
 
Uwaga:  Podczas  wykonania  operacji  może  być  wysłany  wyjątek  klasy  FileNotFoundException  albo 
IOException
 
 

Klasa FileOutputStream 

 
Klasa  FileOutputStream  umożliwia  wykonywanie  operacji  wyprowadzania  danych  na  obiektach 
reprezentujących strumienie wyjściowe.  
 

background image

 

69 

Wykonanie operacji 
 

 

new FileOutputStream(fileName

 
w  której  fileName  jest  wyrażeniem  typu  "String"  określającym  nazwę  pliku,  powoduje  utworzenie  obiektu 
reprezentującego strumień związany z plikiem o podanej nazwie. 
 
Uwaga: Podczas wykonania operacji może być wysłany wyjątek klasy IOException
 
Przykład Kopiowanie pliku 
 

import java.io.*; 
 
public 
class Master { 
    static String fileName = "c:\\autoexec.bat"; 
    public static void main(String args[]) 
        throws IOException 
    { 
        FileInputStream inp; 
        try { 
            inp = new FileInputStream(fileName); 
        } 
        catch(FileNotFoundException e) { 
            System.out.println("File " + fileName +  
                               " not found"); 
            System.in.read(); 
            return; 
        } 
        FileOutputStream out =  
            new FileOutputStream("auto.bat"); 
        int chr, count = 0; 
        while((chr = inp.read()) != -1) { 
            out.write(chr); 
            count++; 
        } 
        System.out.println("Done! " + count +  
                           "chars copied"); 
        System.in.read(); 
    } 

                                         
Wykonanie  programu  powoduje  skopiowanie  pliku  c:\autoexec.bat  do  pliku  auto.bat.  Plik  auto.bat  zostanie 
umieszczony w katalogu bie
żącym, to jest w tym katalogu, z którego pochodzi B-kod aplikacji. 
 
Badanie  końca  pliku  identyfikowanego  przez  odnośnik  inp  należy  wykonać  przez  sprawdzenie,  czy 
wprowadzony  kod  znaku  jest  ró
żny  od  -1  (a  nie  przez  sprawdzenie,  czy  został  wysłany  wyjątek  klasy 
IOException). Z tego powodu zmienna chr musi by
ć typu "int". 
 
 

Klasa StreamTokenizer 

 
Klasa  StreamTokenizer  umożliwia  wprowadzanie  leksemów  ze  strumienia  wejściowego  skojarzonego  z 
plikiem. 
 
Wykonanie operacji 
 

 

new StreamTokenizer(fileStream

 
w której fileStream jest wyrażeniem typu "FileInputStream", powoduje utworzenie obiektu, za pośrednictwem 
którego można ze strumienia pobierać kolejne leksemy. 
 
Uwaga:  W  celu  przygotowania  się  do  pobierania  leksemów  ze  standardowego  strumienia  wejściowego 
(domyślnie: z klawiatury) należy wykonać operację 

background image

 

70 

 

 

new StreamTokenizer(System.in) 

 
a w celu przekazania informacji o końcu strumienia wejściowego nacisnąć klawisz Ctrl-Z
 
 

public int nextToken() 

Metoda  dostarcza  symbol  albo  kod  kolejnego  leksemu.  Jeśli  dostarczy  symbol  TT_NUMBER,  to  leksem  jest 
liczbą umieszczoną w zmiennej nval. Jeśli dostarczy symbol TT_WORD, to leksem jest słowem, odnośnik do 
którego jest umieszczony w zmiennej sval. Symbole TT_EOF TT_EOL oznaczają odpowiednio: koniec pliku 
koniec wiersza.  
 

public void eolIsSignificant(boolean itIs) 

Metoda  określa,  że  mają  być  rozpoznawane  i  dostarczane  znaki  końca  wiersza  (w  przeciwnym  razie  znak 
nowego wiersza jest traktowany tak jak inne odstępy). 
 

public void parseNumbers() 

Metoda  określa,  że  mają  być  rozpoznawane  i  dostarczane  liczby  rzeczywiste  (to  jest  napisy  składające  się  z 
ewentualnego znaku minus oraz cyfr kropki (np. -4.25). 
 

public void quoteChar(int quote) 

Metoda  określa  kod  znaku,  który  ma  być  użyty  jako  obustronny  ogranicznik  łańcucha  znaków  zawierającego 
odstępy  (domyślnie  nie  ma  takiego  ogranicznika).  Po  wprowadzeniu  takiego  łańcucha  metoda  nextToken 
dostarcza kod ogranicznika, a odnośnik do łańcucha jest przypisywany sval
 
 
Przykład Wprowadzanie leksemów 
 

import java.io.*; 
 
public 
class Master { 
    static String fileName = "c:\\source"; 
    public static void main(String args[]) 
        throws IOException 
    { 
        FileInputStream inp; 
        try { 
            inp = new FileInputStream(fileName); 
        } 
        catch(FileNotFoundException e) { 
            System.out.println("File " + fileName +  
                               " not found"); 
            System.in.read(); 
            return; 
        } 
        StreamTokenizer tokens; 
        tokens = new StreamTokenizer(inp); 
        int what,  
            EOF    = StreamTokenizer.TT_EOF, 
            EOL    = StreamTokenizer.TT_EOL, 
            NUMBER = StreamTokenizer.TT_NUMBER, 
            WORD   = StreamTokenizer.TT_WORD; 
        tokens.eolIsSignificant(true); 
        tokens.parseNumbers(); 
        tokens.quoteChar('#'); 
        int lineNo = 1; 
        while((what = tokens.nextToken()) != EOF) { 
            if(what == EOL) 
                System.out.println("\nEnd of line #" +  
                                   lineNo++ + '\n'); 
            else if(what == NUMBER) 
                System.out.print(tokens.nval + " "); 
            else if(what == WORD) 
                System.out.print(tokens.sval + " "); 
            else if(what == '#') 
                System.out.print(tokens.sval + " ") 

background image

 

71 

            else 
                System.out.print(what + " "); 
        } 
        System.in.read(); 
    } 

 
Program analizuje podany plik znakowy, a następnie wyprowadza zawarte w nim leksemy. 
 
Jeśli plik c:\source zawiera napis 
 

 

12   Isa   -4.2e2   #John & Mary# 127 

 

Isa#bell#13 $%& 

 
to zostanie wyprowadzony napis 
 

 

12 Isa -4.2 e2 John & Mary 127 

 

End of line #1 

 

Isa bell 13 $ % & 

 

End of line #2 

 
 

Klasa StringTokenizer 

 
Klasa StringTokenizer umożliwia wprowadzanie leksemów z obiektu łańcuchowego. 
 
Wykonanie operacji 
 

 

new StringTokenizer(string

 
w której string jest wyrażeniem typu "String", powoduje utworzenie obiektu, za pośrednictwem którego można 
z łańcucha pobierać kolejne leksemy. 
 
 

public StringTokenizer(String str, String dlm) 

Argumentami konstruktora są: łańcuch dzielony na leksemy (str) oraz łańcuch składający się z ograniczników 
leksemów (dlm). 
 

public int countTokens() 

Metoda dostarcza liczbę leksemów w łańcuchu. 
 

public boolean hasMoreTokens() 

Metoda dostarcza wartość true, jeśli łańcuch zawiera jeszcze co najmniej jeden leksem. 
 

public String nextToken() throws NoSuchElementException 

Metoda dostarcza kolejny leksem. 
 
 
Przykład Sortowanie imion 
 

import java.io.*; 
import java.util.*; 
 
public 
class Master { 
    static String string = " Jan Ewa Iza "; 
    static String names[]; 
    public static void main(String args[]) 
        throws IOException 
    { 
        StringTokenizer tokens; 
        tokens = new StringTokenizer(string, " \t\n"); 
        int count = tokens.countTokens(); 
        names = new String[count]; 

background image

 

72 

        int pos = 0; 
        while(tokens.hasMoreTokens()) { 
            Object obj = tokens.nextToken(); 
            String str = (String)obj; 
            names[pos++] = str; 
        } 
        show(names); 
        sort(names); 
        show(names); 
        System.in.read(); 
    } 
    static void sort(String names[]) 
    { 
        boolean sorted = false; 
        while(!sorted) { 
            for(int i = 0; i < names.length-1 ; i++) { 
                sorted = true; 
                String one = names[i], 
                       two = names[i+1]; 
                if(one.compareTo(two) > 0) { 
                    names[i]   = two; 
                    names[i+1] = one; 
                    sorted = false; 
                } 
            } 
        } 
    } 
    static void show(String names[]) 
    { 
        for(int i = 0; i < names.length ; i++) 
            System.out.print(names[i] + " "); 
        System.out.println(); 
    } 

 
Ogranicznikami leksemów są: spacja, tabulacja i koniec wiersza. 
 
Wykonanie programu powoduje wyprowadzenie napisu 
 

 

Jan Ewa Iza 

 

Ewa Iza Jan 

 
 

Klasa PrintStream 

 
Klasa PrintStream umożliwia wyprowadzanie ciągów znaków do strumienia wyjściowego
 
Wykonanie operacji 
 

 

new PrintStream(stream

 
w  której  stream  jest  odnośnikiem  do  obiektu  klasy  FileOutputStream  identyfikującego  plik  wyjściowy, 
powoduje utworzenie obiektu, za pośrednictwem którego można do tego pliku wysyłać ciągi znaków. 
 
 

public void print(boolean b) 
public void print(char c) 
public void print(int i) 
public void print(long l) 
public void print(float f) 
public void print(double d) 
public void print(String s) 
public void print(Object o) 

Metoda wyprowadza wartość typu podstawowego albo obiektowego po przetworzeniu jej na ciąg znaków. 
 

background image

 

73 

Uwaga: Zastąpienie identyfikatora print identyfikatorem println powoduje dodatkowo wyprowadzenie znaku 
końca wiersza. 
 

public void println() 

Metoda wyprowadza znak końca wiersza. 
 

public void flush() 

Metoda  wymiata bufor wyjściowy. 
 

public void close() 

Metoda zamyka strumień. 
 
Uwaga: Zamknięcie strumienia jest wykonywane domyślnie w chwili zakończenia wykonywania programu. 
 
 
Przykład Utworzenie pliku tekstowego 
 

import java.io.*; 
 
public 
class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
         PrintStream out = new PrintStream( 
                                  new FileOutputStream( 
                                      "c:\\auto.bat" 
                                  ) 
                          ); 
         out.println("c:\\mouse.exe /q"); 
         out.println("nc"); 
         out.close();             // zb

ę

dne 

         System.in.read(); 
    } 

 
Wykonanie programu powoduje utworzenie pliku c:\auto.bat zawierającego napis 
 

 

c:\mouse.exe /q 

 

nc 

 
 

Klasa plikowa 

 
Równie często jak przesyłanie danych, występuje potrzeba wykonania operacji na pliku albo katalogu. Do tego 
celu doskonale nadaje się klasa plikowa File
 
Metody  klasy  File  umożliwiają  realizowanie  zapytań  o  właściwości  plików  i  katalogów.  Jedna  z  metod 
umożliwia usunięcie pliku, ale żadna nie pozwala na usunięcie katalogu. 
 
Uwaga:  Jeśli  nazwa  pliku  zawiera  separatory  "\"  albo  "/",  to  niezależnie  od  użytego systemu operacyjnego, 
każdy z nich jest uznawany za poprawny
 
 

public File(String name) 

Konstruktor tworzy obiekt plikowy opisujący plik albo katalog o podanej nazwie. 
 

public boolean exists() 

Metoda sprawdza, czy istnieje plik albo katalog identyfikowany przez obiekt plikowy. 
 

public boolean isFile() 

Metoda sprawdza, czy obiekt plikowy identyfikuje plik. 
 

background image

 

74 

public boolean isDirectory() 

Metoda sprawdza, czy obiekt plikowy identyfikuje katalog. 
 

public boolean canRead() 

Metoda sprawdza, czy plik albo katalog może być odczytany. 
 

public boolean canWrite() 

Metoda sprawdza, czy do pliku albo katalogu można dokonać zapisu. 
 

public boolena mkDir() 

Metoda tworzy katalog (wraz z nadkatalogami) o nazwie określonej przez obiekt plikowy.  
 

public boolean renameTo(File trg) 

Metoda zmienia nazwę pliku albo katalogu na podaną. 
 

public boolean delete() 

Metoda usuwa plik. 
 
 
Przykład Klasa plikowa     
 

import java.io.*; 
 
public 
class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
        String path, name; 
        if(args.length != 2) { 
            System.out.println("Please supply Path & Name"); 
            return; 
        } 
        path = args[0]; 
        name = args[1]; 
        String pathName = path + name;  
        File file = new File(pathName); 
        if(!file.exists())  
            throw new FileNotFoundException(pathName); 
        if(file.isFile()) { 
            if(!file.canRead()) { 
                System.out.println( 
                    "File " + pathName + " is not readable" 
                ); 
                return; 
            } else { 
                FileInputStream source =  
                    new FileInputStream(pathName);         
                byte buffer[] = new byte[1024]; 
                while(true) { 
                    int byteCount = source.read(buffer); 
                    if(byteCount == -1) 
                        break; 
                    System.out.write(buffer, 0, byteCount); 
                } 
            } 
        } else { 
            System.out.println( 
                "Directory " + pathName + " contains\n"); 
            String list[] = file.list(); 
            for(int i = 0; i < list.length ; i++) { 
                String fileName = list[i]; 
                System.out.print(fileName); 
                File fullName = new File(pathName + "/" + fileName); 
                if(fullName.isDirectory()) 
                    System.out.print("\t(directory)"); 
                System.out.println(); 
            } 
        } 

background image

 

75 

        System.in.read(); 
    } 

 
Wykonanie programu powoduje wyprowadzenie zawartości podanego pliku albo katalogu. 
 
Jeśli  program  zostanie  wywołany  z  parametrami  określającymi  ścieżkę  (np.  c:\)  i  nazwę  pliku  albo  katalogu 
(np. psp311), to wyprowadzi na przykład napis 
 

 

Directory c:\psp311 contains 

 

PAINT311.ARJ 

 

PAINT311.A01 

 

PAINT311.A02 

 

Paint311 (directory) 

 

 

background image

 

76 

Część IV 

Programowanie obiektowe 

 
 
 
Programowanie obiektowe polega na modelowaniu zjawiskprzedmiotów pojęć za pomocą obiektów klas. Od 
strony  kodowania  programowanie  obiektowe  polega  na  łącznym  wykorzystaniu  hermetyzacji,  dziedziczenia  i 
polimorfizmu.  
 
Podczas  analizy  problemu,  który  ma  być  rozwiązany  techniką  programowania  obiektowego  wyodrębnia  się 
rzeczowniki  opisujące  modelowane  zagadnienie,  a  następnie,  dla  każdego  z  nich,  tworzy  odrębną  definicję 
klasy

 
W szczególności, jeśli zamierza się modelować związki między pojazdamisamochodami silnikami, zaleca się 
utworzyć  definicje  klas  Pojazd  (Vehicle),  Samochód  (Car)  i  Silnik  (Motor),  a  następnie,  posługując  się 
hermetyzacją,  dziedziczeniem  i  polimorfizmem,  wyrazić  wymagane  powiązania  za  pomocą  zestawu  definicji 
klas. 
 
Przykład Projektowanie klas 
 

import java.io.IOException; 
 
public  
class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
        Vehicle vehicle = new Car("Bugatti", 400); 
        String maker = vehicle.theMaker(); 
        int cuInch = vehicle.getCU(); 
        System.out.println(maker + " " + cuInch); 
        // ... 
        System.in.read(); 
    } 

 
abstract 
class Vehicle { 
    abstract public String theMaker(); 
    abstract public int getCU(); 
    // ... 

 
class Car extends Vehicle { 
    private String maker; 
    private Motor motor; 
    public Car(String maker, int cuInch) 
    { 
        this.maker = maker; 
        motor = new Motor(cuInch); 
    } 
    public String theMaker() 
    { 
        return maker; 
    } 
    public int getCU() 
    { 
        return motor.getCU(); 
    } 
    // ... 

 
class Motor { 
    private int cuInch; 
    public Motor(int cuInch) 
    { 
        this.cuInch = cuInch; 
    } 

background image

 

77 

    public int getCU() 
    { 
        return cuInch; 
    } 
    // ... 

 
Ponieważ Samochód jest Pojazdem, a Pojazd ma Silnik, więc związek "jest" wyrażono za pomocą dziedziczenia 
klas, a zwi
ązek "ma" za pomocą zawarcia w klasie. 
 
 
_________________________________________________________________________________________ 

Hermetyzacja 

 
Hermetyzacja polega na odseparowaniu klas i składników klas od innych klas i ich składników. Może dotyczyć 
całych klas oraz pólkonstruktorówmetod funkcji.  
 
 

Hermetyzacja klas 

 
Każda  klasa,  a  wraz  nią  jej  składniki,  należy  do  pewnego  pakietu.  Do  określenia  nazwy  pakietu  służy 
deklaracja 
 

 

package name

 
w której name jest nazwą pakietu.  
 
Deklaracja  pakietu  musi  być  pierwszą  deklaracją  modułu  źródłowego.  Jeśli  jej  nie  użyto,  to  przyjmuje  się,  że 
wszystkie klasy modułu należą do pakietu domyślnego
 
 

Nazwy klas 

 
Klasy mogą być tylko publiczne albo pakietowe. Klasa publiczna jest zadeklarowana ze specyfikatorem public 
i  jest  dostępna  wszędzie.  Klasa  pakietowa  jest  deklarowana  bez  specyfikatora  dostępności.  Jest  ona  wówczas 
dostępna w dowolnym module należącym do jej pakietu. 
 
Jeśli  klasa  Class  należy  do  pakietu  package,  to  jej  nazwą  jest  package.Class.  W  wypadku  odwołania  się  do 
takiej  nazwy,  definicja  klasy  jest  poszukiwana  w  katalogach  wymienionych  w  zmiennej  środowiskowej 
CLASSPATH, w kolejności ich wystąpienia. 
 
W szczególności, jeśli w środowisku Windows 95 wykonano polecenie 
 

 

set CLASSPATH=c:\cafe;d:\debug\tests 

 
a w programie występuje instrukcja 
 

 

janb.pkg Chain chain = new janb.pkg.Chain(); 

 
to  skompilowana  definicja  klasy  Chain  jest  poszukiwana  w  katalogu  c:\cafe\janb\pkg,  a  jeśli  się  jej  tam  nie 
ma, to w katalogu d:\debug\tests\janb\pkg
 
Uwaga: Gdyby w rozpatrywanym programie użyto polecenia importu 
 

 

import janb.pkg.Chain; 

 
albo ogólniejszego polecenia 
 

 

import janb.pkg.*.; 

 

background image

 

78 

to przytoczoną instrukcję można by uprościć do 
 

 

Chain chain = new Chain(); 

 
Przykład Hermetyzacja klas 
 

package janb.pkg; 
 
public 
class Master { 
    // ... 

 
class Slave { 
    // ... 

 

Klasy Master Slave należą do pakietu janb.pkg. Klasa Master jest dostępna wszędzie, natomiast klasa Slave 
jest dost
ępna tylko w obrębie klas należących do pakietu janb.pkg
 
 

Hermetyzacja składników 

 
Hermetyzacja  składników  jest  określona  przez  specyfikatory:  private  (prywatny),  public  (publiczny)  i 
protected (chroniony). 
 
Składnik prywatny jest dostępny tylko w jego klasie macierzystej. Składnik publiczny jest dostępny wszędzie.  
 
Składnik  chroniony  jest  dostępny  w  całym  pakiecie  jego  klasy  macierzystej  oraz  w  podklasie  jego  klasy 
macierzystej (niezależnie od tego, w jakim umieszczono ją pakiecie). 
 
Składnik pakietowy (zadeklarowany bez specyfikatora dostępności) jest dostępny w całym pakiecie, do którego 
należy jego klasa. 
 
Przykład Hermetyzacja składników 
 

package janb.pkg; 
 
class Person { 
    private String name; 
    private int age; 
    public Person(String name, int age) 
    { 
        // ... 
    } 
    protected void setAge(int age) 
    { 
        // ... 
    } 
    static void fun() 
    { 
        // ... 
    } 

 

Pola name i age są prywatne, konstruktor Person jest publiczny, metoda setAge jest chroniona, a funkcja fun 
jest pakietowa (dost
ępna w całym pakiecie janb.pkg). 
 
 
_________________________________________________________________________________________ 

Dziedziczenie 

 
Dziedziczenie wyraża się za pomocą frazy extends. Jest ono zawsze publiczne. Każda klasa jest podklasą tylko 
jednej nadklasy

background image

 

79 

 
U  podstawy  hierarchii  dziedziczenia  znajduje  się  klasa  Object.  Ponieważ  każda  klasa  różna  od  Object  ma 
dokładnie jedną nadklasę, więc struktura klas jest drzewem
 
Przykład Dziedziczenie klas 
 

class Person { 
    private String name; 
    private int age; 
    public Person(String name, int age) 
    { 
        this.name = name; 
        this.age = age; 
    } 

 
class Woman extends Person { 
    boolean isMarried; 
    public Woman(String name, int age,  
                 boolean isMarried) 
    { 
        super(name, age); 
        this.isMarried = isMarried; 
        // ... 
    } 

 
Klasa  Person  jest  nadklasą  klasy  Woman.  Klasa  Woman  jest  podklasą  klasy  Person.  Nadklasą  klasy  Person 
oraz Woman jest klasa Object
 
Obiekt klasy Person zawiera dwie zmienne opisane przez pola name age. Obiekt klasy Woman zawiera takie 
same zmienne jak obiekt klasy Person oraz dodatkowo, zmienn
ą opisaną przez pole isMarried
 
 
_________________________________________________________________________________________ 

Klasy abstrakcyjne 

 
Klasą abstrakcyjną jest klasa zadeklarowana ze specyfikatorem abstract. Jeśli pewna klasa zawiera deklarację 
metody  abstrakcyjnej,  to  jest  metody  zadeklarowanej  ze  specyfikatorem  abstract,  to  musi  być  jawnie 
zadeklarowana jako abstrakcyjna
 
Klasa abstrakcyjna różni się od klasy nieabstrakcyjnej tym, że nie można tworzyć jej obiektów bezpośrednio
to  jest  za  pomocą  operacji  new  albo  za  pomocą  metody  fabrykującej.  Dlatego  klasy  abstrakcyjne  są  z  reguły 
dziedziczone przez klasy nieabstrakcyjne. 
 
Uwaga: Jeśli podklasa klasy abstrakcyjnej nie przedefiniuje wszystkich metod abstrakcyjnych jej nadklasy, to 
sama staje się klasą abstrakcyjną. 
 

public abstract  
class Shape {                  // klasa abstrakcyjna 
    float x, y; 
    Shape(float x, float y) 
    { 
        this.x = x; 
        this.y = y; 
    } 
    public abstract  
    double getArea();          // metoda abstrakcyjna 
    float getX()               // metoda nieabstrakcyjna 
    { 
        return x; 
    } 
    float getY()                      
    { 
        return y; 

background image

 

80 

    } 

 
Klasa Shape zawiera deklarację metody abstrakcyjnej getArea oraz definicje dwóch metod nieabstrakcyjnych. 
Nie wolno pomin
ąć specyfikatora abstract występującego w nagłówku definicji klasy. 
 
 
_________________________________________________________________________________________ 

Metody abstrakcyjne 

 
W  miejscu  ciała  metody  abstrakcyjnej  występuje  średnik.  W  jednej  z  podklas  klasy  abstrakcyjnej  musi  dla 
każdej  metody  abstrakcyjnej  być  dostarczona  metoda  nieabstrakcyjna  o  identycznej  sygnaturze,  której  ciałem 
jest instrukcja grupująca
 
Uwaga: Dwie procedury mają identyczne sygnatury, jeśli mają takie same identyfikatory, a ich listy deklaracji 
parametrów (po usunięciu z nich identyfikatorów) składają się z takich samych jednostek leksykalnych. 
 
W szczególności, dwie następujące procedury, mimo iż są istotnie różne (pierwsza jest funkcją, a druga metodą) 
mają identyczne sygnatury 
 

    static String proc(int one, int[] two[]) 
    String proc(int uno, int[][] due) 

 

abstract  
class Shape {            
    float x, y; 
    Shape(float x, float y) 
    { 
        this.x = x; 
        this.y = y; 
    } 
    public abstract  
    double getArea();          // metoda abstrakcyjna 
    float getX()                          
    { 
        return x; 
    } 
    float getY() 
    { 
        return y; 
    } 

 
public 
class Circle extends Shape { 
    static final double Pi = Math.PI; 
    private float radius; 
    Circle(float x, float y, float radius) 
    { 
        super(x, y); 
        this.radius = radius; 
    } 
    public double getArea()    // metoda nieabstrakcyjna 
    {  
        return Pi * radius * radius; 
    } 

 
W klasie Circle przedefiniowano metodę abstrakcyjną getArea klasy Shape. Gdyby klasa Circle nie zawierała 
definicji metody getArea, to byłaby klas
ą abstrakcyjną
 
 

Zastosowania  

 

background image

 

81 

Następujący  program,  implementujący  stos  o  elementach  niejednorodnych  ilustruje  zasady  posługiwania  się 
klasami i metodami abstrakcyjnymi. 
 
Przykład Projektowanie stosu 
 

import java.io.IOException; 
 
abstract 
class Item { 
    void out() 
    { 
        System.out.println("Please override me!"); 
    } 

 
class IntItem extends Item { 
    private int value; 
    public IntItem(int val) 
    { 
        value = val; 
    } 
    void out() 
    { 
        System.out.print(value + " "); 
    } 

 
class DblItem extends Item { 
    private double value; 
    public DblItem(double val) 
    { 
        value = val; 
    } 
    void out() 
    { 
        System.out.print(value + " "); 
    } 

 
class Stack { 
    private Item vec[]; 
    private int size; 
    private int top = 0; 
    public Stack(int size) 
    { 
        this.size = size; 
        vec = new Item[size]; 
    } 
    void push(Item item) 
        throws StackException 
    { 
        vec[top++] = item; 
        if(top == size) 
            throw new StackException("Stack overflow"); 
    } 
    Item pop() 
        throws StackException 
    { 
        top--; 
        if(top < 0) 
            throw new StackException("Stack underflow"); 
        return vec[top]; 
    } 

 
class StackException extends Exception { 
    public StackException(String cause) 
    { 
        super(cause); 
    } 

 

background image

 

82 

public 
class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
        Stack stack = new Stack(5); 
        try { 
            stack.push(new IntItem(10)); 
            stack.push(new DblItem(2.5)); 
            stack.push(new IntItem(20)); 
        } 
        catch(Exception e) { 
        } 
        while(true) { 
            Item item; 
            try { 
                item = stack.pop(); 
            } 
            catch(StackException e) { 
                break; 
            } 
            item.out(); 
        } 
        System.out.println(); 
        System.in.read(); 
    } 

 

Wykonanie programu powoduje wyprowadzenie napisu 
 

 

20 2.5 10 

 
 
_________________________________________________________________________________________ 

Interfejsy 

 
Każda  klasa  (różna  od  klasy  Object)  ma  tylko  jedną  nadklasę,  ale  może  implementować  dowolnie  wiele 
interfejsów.  
 
Interfejs  jest  w  istocie  klasą  abstrakcyjną,  zawierającą  tylko  definicje  statycznych  pól  ustalonych  (static  
final) oraz metod abstrakcyjnych (abstract). 
 
Uwaga:  Jeśli  w  definicji  interfejsu  pominie  się  specyfikatory  static,  final  i  abstract,  to  zostaną  one 
domniemane. 
 
Przykład Definicja interfejsu 
 

interface Drawable { 
    static final Color red = Color.red; 
    final Color green = Color.green;  
    Color blue = Color.blue; 
    abstract void draw(Graphics gDC); 
    Graphics getDC(); 

 
Wszystkie 3 pola są statyczne i ustalone. Obie metody są abstrakcyjne. 
 
 

Implementowanie interfejsu 

 
Implementowanie interfejsu wyraża się za pomocą słowa kluczowego implements. Każdemu odnośnikowi typu 
interfejsowego można przypisać odniesienie do obiektu klasy implementującej ten interfejs. 
 
Interfejs  może  zawierać  tylko  deklaracje  metod  oraz  definicje  statycznych  zmiennych  ustalonych  (final). 
Każdy z tych składników klasy jest domyślnie publiczny

background image

 

83 

 
Klasa,  która  implementuje  interfejs  musi  dostarczyć  definicje  wszystkich  jego  metod.  W  przeciwnym  razie 
staje się klasą abstrakcyjną. 
 
Przykład Implementowanie interfejsu 
 

import java.io.IOException; 
 
interface Taxable { 
    double amount(); 

 
interface Eligible { 
    boolean isMarried(); 

 
class Person { 
    private String name; 
    private int age; 
    protected double taxes; 
    public Person(String name, int age) 
    { 
        // ... 
    } 
    // ... 

 
class Woman extends Person  
            implements Taxable, Eligible { 
    private Person husband; 
    public Woman(String name, int age, Person husband) 
    { 
        super(name, age); 
        this.husband = husband; 
    } 
    public double amount() 
    { 
        return taxes; 
    }  
    public boolean isMarried() 
    { 
        return husband != null; 
    } 
    // ... 

 
public 
class Master { 
    public static void main(String args[]) 
    { 
        Taxable taxRef; 
        Person john = new Person("John Smith", 40); 
        Woman mary = new Woman("Mary Smith", 20, john); 
        taxRef = mary; 
        double taxes = taxRef.amount(); 
        // ... 
        System.in.read(); 
    } 

 
Klasa Woman dziedziczy klasę Person oraz implementuje interfejsy Taxable Eligible. 
 
Odnośnikowi interfejsowemu taxRef przypisano odniesienie do obiektu klasy Woman implementującej interfejs 
Taxable
 
Metod
ę amount wywołano poprzez odnośnik typu "Taxable". 
 
 

background image

 

84 

Zastosowania  

 
Następujący  program,  implementujący  stos  o  elementach  niejednorodnych,  ilustruje  zasadę  posługiwania  się 
interfejsami. 
 
Przykład Projektowanie stosu 
 

import java.io.IOException; 
 
interface Stackable { 
    void out(); 

 
class IntItem implements Stackable { 
    private int value; 
    public IntItem(int val) 
    { 
        value = val; 
    } 
    void out() 
    { 
        System.out.print(value + " "); 
    } 

 
class DblItem implements Stackable { 
    private double value; 
    public DblItem(double val) 
    { 
        value = val; 
    } 
    void out() 
    { 
        System.out.print(value + " "); 
    } 

 
class Stack { 
    private Stackable vec[]; 
    private int size; 
    private int top = 0; 
    public Stack(int size) 
    { 
        this.size = size; 
        vec = new Stackable[size]; 
    } 
    void push(Stackable item) 
        throws StackException 
    { 
        vec[top++] = item; 
        if(top == size) 
            throw new StackException("Stack overflow"); 
    } 
    Stackable pop() 
        throws StackException 
    { 
        top--; 
        if(top < 0) 
            throw new StackException("Stack underflow"); 
        return vec[top]; 
    } 

 
class StackException extends Exception { 
    public StackException(String cause) 
    { 
        super(cause); 
    } 

 
public 

background image

 

85 

class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
        Stack stack = new Stack(5); 
        try { 
            stack.push(new IntItem(10)); 
            stack.push(new DblItem(2.5)); 
            stack.push(new IntItem(20)); 
        } 
        catch(Exception e) { 
        } 
        while(true) { 
            Stackable item; 
            try { 
                item = stack.pop(); 
            } 
            catch(StackException e) { 
                break; 
            } 
            item.out(); 
        } 
        System.out.println(); 
        System.in.read(); 
    } 

 

Wykonanie programu powoduje wyprowadzenie napisu 
 

 

20 2.5 10 

 
 
_________________________________________________________________________________________ 

Konwersje 

 
Konwersja  obiektowa  jest  przekształceniem  z  jednego  typu  obiektowego  do  innego.  Jest  ona  wyrażana  przez 
operator konwersji. 
 

 

(Type

 
w którym Type jest nazwą typu docelowego (np. "Object" albo "String []"). 
 
Rezultatem poprawnej konwersji odnoœnikowej jest odnoœnik zainicjowany wartoœci¹ argumentu.  
 
Na przykład 
 

import java.awt.*; 
import java.applet.*; 
 
public 
class Master { 
    // ... 
    Applet fun(Object obj) 

   { 

        return (Applet)(Panel)obj; 
    } 

 
Wyrażenie obj poddano konwersji do typu "Panel", a następnie do typu "Applet". 
 
 

Konwersje poprawne 

 
Konwersja odnoœnikowa jest poprawna tylko wówczas gdy jest dopuszczalna wykonalna
 

background image

 

86 

Konwersja jest dopuszczalna gdy jest to¿samoœciowa (np. z typu "Vector" do "Vector"), albo gdy polega na 
przekszta³ceniu  z  klasy  do  nadklasy  (np.  z  "Vector"  do  "Object"),  z  klasy  do  podklasy  (np.  z  "Object"  do 
"Vector") albo z klasy do implementowanego przez ni¹ interfejsu (np. z "Vector" do "Cloneable"). 
 
Konwersja jest wykonalna, gdy jest dopuszczalna, a wyra¿enie 
 

 

exp instanceof Type 

 
(por. opis operatora instanceof) ma wartoœæ true
 
Na przyk³ad 
 

String city = "Warsaw"; 
Object obj = (Object)city; 
city = (String)obj; 
Vector vec = (Vector)obj;  // bł

ą

d (konwersja niewykonalna) 

 
Ponieważ klasa String jest podklasą klasy Object, więc konwersja 
 

(String)obj 

 
jest dopuszczalna. 
 
Jest ona wykonalna, poniewa
ż jest dopuszczalna, a wyrażenie 
 

obj instanceof String 

 
ma wartość true
 
Konwersja odno
śnikowa 
 

(Vector)obj 

 
 jest dopuszczalna, ale nie jest wykonalna. A zatem jest błędna. 
 
 

Konwersje standardowe 

 
Konwersją  standardową  jest  każda  konwersja,  która  może  być  wykonana  niejawnie.  Do  tej  kategorii  należą 
konwersje arytmetycznełańcuchowe obiektowe
 
 

Konwersje arytmetyczne 

Standardową  konwersją  arytmetyczną  jest  przekształcenie  z  typu  o  węższym  zakresie  wartości  do  typu  o 
szerszym zakresie wartości (np. z typu "int" do typu "long"). 
 
Na przykład 
 

byte b = 12; 
int i = 12; 
i = b;            // i = (int)b; 
b = i;            // bł

ą

d (brak konwersji) 

 
 

Konwersje łańcuchowe 

Standardową  konwersją  łańcuchową  jest  przekształcenie  z  dowolnego  typu  podstawowego  oraz  obiektowego 
do typu "String". 
 

background image

 

87 

Jeśli  operacja  dotyczy  zmiennej  typu  podstawowego,  to  jest  używana  metoda  append  klasy  StringBuffer,  a 
jeśli  dotyczy  zmiennej  typu  obiektowego,  to  sposób  wykonania  konwersji  jest  określony  przez  jej  metodę 
toString
 
Uwaga: Metoda toString jest zdefiniowana zawsze, ponieważ występuje w klasie Object
 
Na przykład 
 

Thread thread = Thread.currentThread(); 
String var = 4 + "sale" + thread; 

 
Operacja 
 

 

4 + "sale" + thread 

 
jest wykonywana tak jak operacja 
 

 

new StringBuffer().append(4). 

 

                   append("sale"). 

 

                   append(thread.toString()). 

 

                   toString() 

 
 

Konwersje obiektowe 

Standardową  konwersją  obiektową  jest  przekształcenie  odnośnika  klasy  na  odnośnik  jego  nadklasy  oraz 
odnośnika  klasy  na  odnośnik  jej  interfejsu.  W  szczególności  taką  konwersją  jest  przekształcenie  każdego 
odnośnika na odnośnik klasy Object
 
Na przykład 
 

import java.io.IOException; 
 
class Primary implements Runnable { 
    // ... 
    public void run() 
    { 
        // ... 
    } 

 
class Derived extends Primary { 
    // ... 

 
public 
class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
        Primary prm; 
        prm = (Primary)new Derived(); 
        prm = new Derived();      // identyczne z poprzednim 
 
        Runnable rnb; 
        rnb = (Runnable)prm; 
        rnb = prm;                // identyczne z poprzednim; 
 
        rnb = (Runnable)new Derived(); 
        rnb = new Derived();      // identyczne z poprzednim 
 
        prm = (Primary)rnb; 
        prm = rnb;                // bł

ą

d (brak konwersji) 

 
        args = (String [3])args;  // bł

ą

d (podano rozmiar) 

        // ... 
        System.in.read(); 
    } 

background image

 

88 

 
Ponieważ  klasa  Primary  implementuje  interfejs  Runnable,  więc  albo  musi  być  abstrakcyjna,  albo  musi 
zawiera
ć definicję metody run (wybrano to drugie). 
 
ąd w operacji przypisania 
 

prm = rnb 

 
wynika z tego, 
że nie istnieje konwersja standardowa z typu "Runnable" do typu "Primary". 
 
ąd w operacji 
 

(String [3])args 

 
polega na tym, że użyto rozmiaru tablicy. 
 
 

Konwersje statyczne 

 
Każda konwersja odnośnika na odnośnik jego nadklasy albo podklasy jest konwersją poprawną statycznie, ale 
nie musi być konwersją poprawną dynamicznie (tj. podczas wykonania programu). 
 
Przykład Konwersje statyczne  
 

String str = "Hello"; 
Object obj = str;         // Object obj = (Object)str; 
str = (String)obj; 

 
Ponieważ klasa String jest podklasą klasy Object, więc wyrażenie str typu "String" może być przekształcone w 
wyra
żenie typu "Object", a wyrażenie obj typu "Object" może być przekształcone w wyrażenie typu "String". 
 
 

Konwersje dynamiczne 

 
Konwersja  odnośnika  na  inny  odnośnik  jest  poprawna  dynamicznie  tylko  wówczas,  gdy  klasa  odniesienia 
przypisanego  odnośnikowi  poddawanemu  konwersji  jest  klasą  albo  podklasą  odnośnika  docelowego.  Jeśli 
wymaganie to nie jest spełnione, to z miejsca konwersji jest wysłany wyjątek klasy ClassCastException
 
Przykład Konwersje dynamiczne 
 

import java.io.IOException; 
import java.awt.*; 
import java.applet.*; 
 
public 
class Master { 
    public static void main(String args[]) 
    { 
        Panel  panel; 
        Applet applet; 
        panel  = new Applet(); 
        applet = (Applet)panel; 
        panel  = new Panel(); 
        applet = (Applet)panel;      // bł

ą

d dynamiczny 

        // ... 
        System.in.read(); 
    } 

 
Ponieważ klasa Applet jest podklasą klasy Panel, więc obie instrukcje 
 

applet = (Applet)panel; 

background image

 

89 

 
s
ą poprawne statycznie
 
Poniewa
ż podczas wykonywania drugiej instrukcji  
 

applet = (Applet)panel;  

 
odnośnik panel nie identyfikuje obiektu klasy Applet, więc podczas próby wykonania konwersji  
 

(Applet)panel 

 
zostanie wysłany wyj
ątek klasy ClassCastException
 
 

Operator pochodzenia 

 
Rozstrzygnięcie  o  tym,  czy  konwersja  doprowadzi,  czy  nie  doprowadzi  do  wysłania  wyjątku  klasy 
ClassCastException umożliwia operator pochodzenia
 
Operacja pochodzenia ma postać 
 

 

exp instanceof Class 

 
w której exp jest wyrażeniem odnośnikowym, a Class jest identyfikatorem klasy. 
 
Rezultatem  operacji  pochodzenia  jest  orzecznik,  który  ma  wartość  true  tylko  wówczas,  gdy  odnośnik  exp 
identyfikuje obiekt klasy Class albo jej podklasy. 
 
Przykład Operator pochodzenia 
 

static Applet anApplet(Panel panel) 

    if(panel instanceof Applet) 
        return (Applet)panel; 
    return null; 

 
Jeśli podczas wykonywania funkcji fun, odnośnik panel nie identyfikuje obiektu, który jest klasy Applet albo jej 
podklasy, to funkcja zwraca null
 
 
_________________________________________________________________________________________ 

Polimorfizm 

 
Każde  wywołanie  metody  jest  polimorficzne.  Oznacza  to,  że  jest  wywoływana  metoda  widoczna  w  klasie 
obiektu  identyfikowanego  przez  odniesienie  przypisane  odnośnikowi,  a  nie  metoda  widoczna  w  klasie 
odnośnika. 
 
Przykład Wywołania polimorficzne 
 

interface Taxable { 
    void showTaxes(); 

 
class Person { 
    private double taxes; 
    public Person(String name, int age) 
    { 
        // ... 
    } 
    double getTaxes() 
    { 

background image

 

90 

        return taxes; 
    } 
    void showTaxes() 
    { 
        // ... 
    } 

 
class Woman extends Person implements Taxable { 
    public Woman(String name, int age) 
    { 
        super(name, age); 
    } 
    public void showTaxes() 
    { 
        // ... 
    }  
    // ... 

 
class Master { 
    void Sub(Person person, Woman woman, Taxable taxes) 
    { 
        person = new Woman("Mary Smith", 20); 
        person.showTaxes(); 
        // ... 
        woman = (Woman)person; 
        taxes = woman; 
        taxes.showTaxes(); 
        // ... 
        double amount; 
        amount = person.getTaxes(); 
    } 

 
Wykonanie instrukcji 
 

person.showTaxes(); 
 

powoduje wywołanie metody showTaxes klasy Woman (a nie metody showTaxes klasy Person). 
 
Wykonanie instrukcji  
 

taxes.showTaxes() 

 
powoduje wywołanie metody showTaxes klasy Woman (a nie metody showTaxes interfejsu Taxable). 
 
Wykonanie instrukcji 
 

amount = person.getTaxes(); 

 
powoduje wywołanie metody getTaxes widocznej w klasie Woman, to jest metody getTaxes klasy Person
 
 
_________________________________________________________________________________________ 

Klasy 

 
Zasady  programowania  obiektowego  można  zilustrować  projektami  klas  do  przechowywania  łańcuchów 
znaków (CString) i dynamicznych wektorów (CVector).  
 
 

Klasa CString 

 
Klasa  CString  umożliwia  tworzenie  obiektów  identyfikujących  łańcuchy  znaków  o  dowolnym  rozmiarze.  W 
odróżnieniu  od  predefiniowanej  klasy  String,  której  obiekty  identyfikują  wektory  o  elementach  2-bajtowych, 

background image

 

91 

obiekty  klasy  CString  identyfikują  wektory  o  elementach  1-bajtowych,  a  więc  zapewniają  oszczędniejszą 
implementację. 
 

class CString implements Cloneable {  
    private int len; 
    private byte ref[]; 
    public CString(String str) 
    { 
        len = str.length(); 
        ref = new byte [len+1]; 
        for(int i = 0; i < len ; i++) 
            ref[i] = (byte)str.charAt(i); 
        ref[len] = 0; 
    } 
    public char charAt(int i) 
        throws IndexOutOfBoundsException 
    { 
        if(i < 0 || i >= len) 
            throw new IndexOutOfBoundsException(); 
        return (char)ref[i]; 
    } 
    public String toString() 
    { 
        String str = ""; 
        for(int i = 0; i < len ; i++) 
            str += (char)ref[i]; 
        return str; 
    } 
    public int compareTo(CString str) 
    { 
        return toString().compareTo(str.toString()); 
    } 
    public boolean equals(Object obj) 
    { 
        if(obj instanceof String) { 
            String str = ((String)obj).toString(); 
            return toString().compareTo(str) == 0; 
        } else 
            return false; 
    } 
    public Object clone() 
        throws CloneNotSupportedException 
    { 
        Object clone = super.clone(); 
        String str = toString(); 
        CString string = new CString(str); 
        ((CString)clone).ref = string.ref; 
        return clone; 
    } 
    public void set(CString str) 
        throws CloneNotSupportedException 
    {        
        CString string = (CString)str.clone(); 
        len = string.len; 
        ref = string.ref; 
    } 
    public void set(String str) 
        throws CloneNotSupportedException 
    { 
        CString string = new CString(str); 
        set(string); 
    } 

 
Przykład Użycie klasy CString  
 

import java.io.IOException; 
 
public  
class Master { 
    public static void main(String args[]) 

background image

 

92 

        throws IOException, CloneNotSupportedException 
    { 
        CString greet = new CString("Hello world"); 
        System.out.println(greet); 
        int i = 0; 
        while(true) { 
            try { 
                char chr = greet.charAt(i++); 
                System.out.print(chr); 
            } 
            catch(IndexOutOfBoundsException e) { 
                break; 
            } 
        } 
        System.out.println(); 
        CString string = (CString)greet.clone(); 
        System.out.println(string.equals(greet.toString())); 
        greet = new CString("Hello"); 
        System.out.println(greet); 
        System.in.read(); 
    } 

 
Wykonanie programu powoduje wyprowadzenie napisu 
 

 

Hello world            

 

Hello world 

 

true 

 

Hello 

 
 

Klasa CVector 

 
Klasa  CVector  umożliwia  tworzenie  obiektów  identyfikujących  dynamiczne  wektory  o  elementach 
pomocniczej klasy CDouble. Klasa ta stanowi otoczkę dla zmiennych typu "double". 
 

public  
class CVector { 
    private int size = 1; 
    private int len = 0; 
    private CDouble ref[]; 
    public CVector() 
    { 
        ref = new CDouble [size]; 
    } 
    CDouble refAt(int i) 
    { 
        return ref[i]; 
    } 
    CVector append(double val) 
    { 
        if(len >= size) { 
            CDouble ref[] = new CDouble [2 * size]; 
            for(int i = 0; i < size ; i++) 
                ref[i] = this.ref[i]; 
            size *= 2; 
            this.ref = ref; 
        } 
        ref[len++] = new CDouble(val); 
        return this; 
    } 

  
class CDouble { 
    private double value; 
    public CDouble(double val) 
    { 
        value = val; 
    }            
    public double get() 

background image

 

93 

    { 
        return value; 
    } 
    public void set(double val) 
    { 
        value = val; 
    } 

 
Przykład Użycie klasy CVector 
 

import java.io.IOException; 
import java.util.*; 
 
public  
class Master { 
    private static final int size = 5; 
    public static void main(String args[]) 
        throws IOException 
    { 
        CVector vec = new CVector(); 
        for(int i = 0; i < size ; i++) 
            vec.append(10 * (i+1)); 
        for(int i = 0; i < size ; i++) 
            vec.refAt(i).set(-vec.refAt(i).get()); 
        for(int i = 0; i < size ; i++) 
            System.out.print(vec.refAt(i).get() + " "); 
        System.out.println(); 
        System.in.read(); 
    } 

 
Wykonanie programu powoduje wyprowadzenie napisu 
 

 

-10 -20 -30 -40 -50 

 
 
_________________________________________________________________________________________ 

Kolekcje 

 
Następujący  program,  w  którym  zdefiniowano  interfejs  Measurable,  kolekcję  Shapes  oraz  iterator 
ShapesEnumerator ilustruje zasady programowania obiektowego. 
 
Wymienione klasy umożliwiają przechowywanie obiektów podklas klasy Shape, bezpośrednio albo pośrednio 
implementujących interfejs Measurable
 
Przykład  Projektowanie kolekcji 

 
interface Measurable { 
    double getArea();           // wyznaczenie pola 
    String kindOf();            // dostarczenie nazwy  

 
class Shapes {         
    private int noOfSlots = 1;  // liczba miejsc 
    private int freeSlot  = 0;  // pierwsze wolne miejsce 
    Measurable vec[];           // odno

ś

niki do obiektów 

    public Shapes() 
    { 
        vec = new Measurable [noOfSlots]; 
    } 
    public int getFreeSlot() 
    { 
        return freeSlot; 
    } 
    int getNoOfSlots() 
    { 
        return noOfSlots; 

background image

 

94 

    } 
    Measurable getAt(int pos) 
    { 
        return vec[pos]; 
    } 
    int addShape(Measurable shape)  // dodaj obiekt 
    { 
        if(freeSlot >= noOfSlots) { 
            Measurable vecOld[] = vec; 
            int noOfSlotsOld = noOfSlots; 
            noOfSlots <<= 1;        // podwój pojemno

ść

 

            vec = new Measurable [noOfSlots]; 
            System.arraycopy(vecOld, 0,  
                             vec, 0, vecOld.length); 
        } 
        vec[freeSlot] = shape; 
        return freeSlot++; 
    } 
    Measurable remShape(int pos)    // usu

ń

 obiekt 

        throws IndexOutOfBoundsException 
    { 
        Measurable shape = vec[pos]; 
        vec[pos] = null; 
        return shape; 
    }     
    double getArea()                // wyznacz pole 
    { 
        double total = 0; 
        for(int i = 0; i < freeSlot ; i++) 
            if(vec[i] != null) 
                total += vec[i].getArea(); 
        return total; 
    } 
    void showAll()                  // poka

ż

 zawarto

ść

 

    { 
        for(int i = 0; i < freeSlot ; i++) { 
            Shape shape = (Shape)vec[i]; 
            if(shape != null) 
                System.out.println( 
                               "\t" + shape.kindOf() + 
                               " At(" +  
                               shape.getX()  +  
                               "," +  
                               shape.getY() +   
                               ")" 
                           ); 
        } 
    } 
    public final synchronized Enumeration elements() 
    { 
        return new ShapesEnumerator(this); 
    } 

 
class ShapesEnumerator implements Enumeration { 
    private Shapes shapes; 
    private int pos = 0; 
    public ShapesEnumerator(Shapes shapes) 
    { 
        this.shapes = shapes; 
    } 
    public boolean hasMoreElements() 
    { 
        if(pos < shapes.getFreeSlot())   
            return true; 
        pos = 0; 
        return false; 
    } 
    public Object nextElement() 
    { 
        if(hasMoreElements()) 
            return shapes.getAt(pos++); 
        throw new NoSuchElementException("ShapesEnumerator"); 

background image

 

95 

    } 

 
Zdefiniowane powyżej klasy kolekcyjne i iteracyjne zostały użyte w następującym programie. W programie tym, 
podklasami klasy Shape, implementuj
ącymi interfejs Measurable są Circle i Square

 
import java.io.*; 
import java.util.*; 
 
abstract  
class Shape implements Measurable { 
    private int x = 0, y = 0;          // współrz

ę

dne 

ś

rodka 

    Shape() 
    { 
    } 
    Shape(int x, int y) 
    { 
        this.x = x; 
        this.y = y; 
    } 
    int getX() 
    { 
        return x; 
    } 
    int getY() 
    { 
        return y; 
    } 
    public abstract double getArea();  // powierzchnia obiektu 
    public String kindOf()             // nazwa obiektu 
    { 
        return getClass().toString(); 
    } 

 
class Circle extends Shape {           // klasa okr

ę

gów 

    public static final double Pi = Math.PI; 
    private int radius; 
    public Circle(int x, int y, int radius) 
    { 
        super(x, y); 
        this.radius = radius; 
    } 
    public String kindOf() 
    { 
        return "Circle"; 
    } 
    public double getArea() 
    { 
        return Pi * radius * radius; 
    } 

 
class Square extends Shape {           // klasa kwadratów    
    private int side; 
    public Square(int x, int y, int side) 
    { 
        super(x, y); 
        this.side = side; 
    } 
    public String kindOf() 
    { 
        return "Square"; 
    } 
    public double getArea() 
    { 
        return (double)side * side; 
    } 

 
public  
class Master { 

background image

 

96 

    public static void main(String args[]) 
        throws IOException 
    { 
        Circle aCircle = new Circle(10, 10, 1),  // aC 
               bCircle = new Circle(20, 20, 2),  // bC 
               cCircle = new Circle(30, 30, 3);  // cC 
        Square aSquare = new Square(40, 40, 5),  // aS 
               bSquare = new Square(50, 50, 5);  // bS 
 
        Shapes shapes = new Shapes(); 
        int pos;   
                                 // obiekty w kolekcji 
                                         // ========== 
        shapes.addShape(aCircle);        // aC 
        pos = shapes.addShape(bCircle);  // aC, bC 
        shapes.remShape(pos);            // aC 
        pos = shapes.addShape(cCircle);  // aC, cC 
        shapes.addShape(aSquare);        // aC, cC, aS 
        shapes.remShape(pos);            // aC, aS 
        pos = shapes.addShape(bSquare);  // aC, aS, bS 
        shapes.remShape(pos);            // aC, aS 
 
        System.out.println("Shapes in collection:\n"); 
        shapes.showAll(); 
        System.out.println("\nTotal area: " + shapes.getArea()); 
        System.out.println("\nCleaning the collection:\n"); 
        ShapesEnumerator shapesEnum =  
            new ShapesEnumerator(shapes); 
        pos = 0; 
        while(shapesEnum.hasMoreElements()) { 
            Shape shape = (Shape)shapesEnum.nextElement();  
            if(shape != null) 
                System.out.println( 
                               "\tDeleting " + 
                               shape.kindOf() + 
                               ", Area: " + 
                               shape.getArea()  
                           ); 
            shapes.remShape(pos++); 
        } 
        System.out.println("\nDone!"); 
        System.in.read(); 
    } 

 
Program wyprowadza napis pokazany w tabeli Wyniki wykonania
 
Tabela Wyniki wykonania 

### 
 

Shapes in collection:      

                         
 

Circle At(10,10)  

 

Square At(40,40)  

                         
 

Total area: 28.1416        

                         
 

Cleaning the collection:   

                         
 

Deleting Circle, Area: 3.14159 

 

Deleting Square, Area: 25 

                         
 

Done!   

### 

 

background image

 

97 

Część V 

Programowanie zdarzeniowe 

 
Programowanie  zdarzeniowe  polega  na  dostarczeniu  zestawu  procedur,  z  których  po  zajściu  zdarzenia 
zewn
ętrznego (kliknięciu przycisku myszki, naciśnięciu klawisza klawiatury), jest wybierana do wykonania ta 
procedura, która została przewidziana do obsługi zdarzenia. 
 
Podstawowymi procedurami zdarzeniowymi, w które wyposaża się komponenty pojemniki komponentów (np. 
przyciskiapletyramki) są metody paint handleEvent.  
 
Metoda  paint  jest  wywoływana  przez  System  po  utworzeniu  pojemnika  oraz  każdorazowo  gdy  zawartość 
pojemnika powinna zostać odtworzona. Z wnętrza programu może być wywołana za pomocą metody repaint.  
 
Uwaga:  Metoda  repaint  wywołuje  metodę  update,  która  tuż  przez  wywołaniem  metody  paint  czyści  obszar 
pojemnika (wypełnia go kolorem tła). 
 
Metoda  handleEvent  jest  wywoływana  każdorazowo  po  zajściu  zdarzenia  zewnętrznego.  Jeśli  w  klasie 
pojemnika nie zostanie przedefiniowana, to w zależności od rodzaju zdarzenia wywoła takie metody jak action 
(np.  reakcja  na  kliknięcie  przycisku),  mouseDown  (reakcja  na  naciśnięcie  przycisku  myszki),  mouseUp 
(reakcja na zwolnienie przycisku myszki), itp. 
 
Uwaga:  Jeśli  zdarzenie  dotyczące  sterownika  (komponentu  albo  pojemnika)  zostało  w  pełni  obsłużone  przez 
metodę  (np.  handleEvent,  action,  itp.),  to  potwierdzeniem  tego  jest  zwrócenie  przez  nią  wartości  true.  W 
przeciwnym razie metoda zwraca wartość false. Umożliwia to obsłużenie zdarzenia przez pojemnik sterownika. 
 
 

public void paint(Graphics gDC) 

Metoda jest wywoływana przez System, gdy zachodzi potrzeba odtworzenia zawartości pulpitu. 
 

public void repaint() 
public void repaint(int x, int y, int w, int h) 

Metoda  jest  apelem  o  niejawne  wywołanie  metody  update  w  odniesieniu  do  całego  pulpitu,  albo  do  jego 
prostokątnej części określonej łącznie przez (x,y) i h
 

public boolean handleEvent(Event evt) 

Metoda jest wywoływana przez System, bezpośrednio po zajściu zdarzenia zewnętrznego. 
 
 
Następujący  program,  pokazany  podczas  wykonywania  na  ekranie  Propagacja  zdarzeń,  ilustruje  zasadę 
obsługiwania zdarzeń. 
 
Ekran Propagacja zdarzeń 

### propag.gif 

 
Przykład Propagacja zdarzeń 
 

import java.io.IOException; 
import java.awt.*; 
 
public 
class Master extends Frame { 
    static Frame frame; 
    static Button button; 
    public Master(String caption) 
    { 
        super(caption); 
    } 
    public static void main(String args[]) 
        throws IOException 
    { 
        frame = new Master("Frame window"); 
        button = new MyButton("Click me", frame); 
        frame.setLayout(new FlowLayout()); 

background image

 

98 

        frame.add(button); 
        frame.resize(120, 80); 
        frame.show(); 
        System.out.println("Press Enter to close all"); 
        System.in.read(); 
        System.exit(0); 
    } 
    public boolean action(Event evt, Object arg) 
    { 
        Graphics gDC = frame.getGraphics(); 
        gDC.drawString("Frame clicked!", 10, 70); 
        return true; 
    } 

 
class MyButton extends Button { 
    Frame frame; 
    MyButton(String caption, Frame frame) 
    { 
        super(caption); 
        this.frame = frame; 
    } 
    public boolean action(Event evt, Object arg) 
    { 
        Graphics gDC = frame.getGraphics(); 
        gDC.drawString("Button clicked!", 10, 50); 
        return false; 
    } 

 
Kliknięcie przycisku powoduje wywołanie metody action klasy MyButton.  
 
Ponieważ metoda zwraca false, więc zdarzenie propaguje do pojemnika przycisku, którym jest okno aplikacji. 
 
Pojemnik zwraca wartość true, co oznacza, że zdarzenie w pełni obsłużono. 
 
 
_________________________________________________________________________________________ 

Klasa zdarzeniowa 

 
W ciele metody handleEvent jest dostępny odnośnik do obiektu klasy Event, zawierającego szczegółowy opis 
zaistniałego zdarzenia. 
 
Wybrany fragment definicji klasy Event przytoczono w tabeli Klasa zdarzeniowa
 
Tabela Klasa zdarzeniowa 

### 
public 
class Event extends Object { 
    public int id;           // rodzaj zdarzenia 
    public Object target;    // odno

ś

nik do sterownika 

    public Object arg;       // cecha sterownika 
    public long when;        // chwila zaj

ś

cia zdarzenia 

    public int x;            // punkt klikni

ę

cia (x) 

    public int y;            // punkt klikni

ę

cia (y) 

    public int key;          // kod klawisza 
    public int modifiers;    // Ctrl, Shift, Alt, Meta 
    public int clickCount;   // licznik klikni

ęć

 

    public Event evt;        // zdarzenie 
    // ... 

### 

 
Następujący  program,  pokazany  podczas  wykonania  na  ekranie  Pozdrowienie,  wyświetla  przyciski  Greet  i 
Clear.  Po  kliknięciu  przycisku  jest  wywoływana  metoda  handleEvent.  Jeśli  kliknięto  przycisk  Greet,  to 
wyświetla się napis Hello, a jeśli kliknięto przycisk Clear to następuje wyczyszczenie pulpitu ramki. 

background image

 

99 

 
Ekran Pozdrowienie 

### sayhello.gif 

 

import java.io.IOException; 
import java.awt.*; 
 
public  
class Master extends Frame { 
    static private Button greetButton, clearButton; 
    String msg = ""; 
    static Frame frame; 
    public static void main(String args[]) 
        throws IOException 
    { 
        frame = new Master(); 
        frame.setLayout(new FlowLayout()); 
        frame.resize(160, 100); 
        frame.show(); 
        System.in.read(); 
        System.exit(0); 
    } 
    Master() 
    { 
        super("Hello"); 
        greetButton = new Button("Greet"); 
        clearButton = new Button("Clear"); 
        add(greetButton); 
        add(clearButton); 
    } 
    public void paint(Graphics gDC) 
    { 
        gDC.drawString(msg, 15, 50); 
    } 
    public boolean handleEvent(Event evt) 
    { 
        if(evt.id == Event.ACTION_EVENT) { 
            if(evt.target == greetButton) { 
                msg = "Hello"; 
                repaint();    // "wywołuje" paint 
                return true; 
            } else if(evt.target == clearButton) { 
                msg = ""; 
                repaint();    // "wywołuje" paint 
                return true; 
            } 
        } 
        return false; 
    } 

 
Ponieważ  przedefiniowano  metodę  handleEvent,  więc  nie  są  wywoływane  takie  metody  jak  action
mouseDownmouseUp, itp. 
 
Wywołanie  metody  repaint  jest  apelem  o  wywołanie  metody  paint.  System  uwzględni  ten  apel  w  najbliższym 
dogodnym momencie, ale dopiero po zako
ńczeniu wykonywania procedury wywołującej. 
 
 
_________________________________________________________________________________________ 

Obsługiwanie zdarzeń 

 
Przedefiniowanie  procedury  handleEvent  stanowi  najogólniejszy  sposób  obsługi  zdarzeń.  Nie  zawsze  jest  to 
jednak  konieczne,  ponieważ  w  wielu  wypadkach  wystarczy  przedefiniować  procedury  obsługi  wywoływane 
przez predefiniowaną metodę handleEvent
 
Fragment  klasy  Component  zawierającej  definicję  metody  handleEvent  przytoczono  w  tabeli  Metoda 
handleEvent

background image

 

100 

 
Tabela Metoda handleEvent 

### 
public abstract 
class Component implements ImageObserver { 
    // ... 
    public boolean handleEvent(Event evt)  
    { 
        switch (evt.id) { 
            case Event.ACTION_EVENT: 
                return action    (evt, evt.arg); 
            case Event.MOUSE_DOWN: 
                return mouseDown (evt, evt.x, evt.y); 
            case Event.MOUSE_UP: 
                return mouseUp   (evt, evt.x, evt.y); 
            case Event.MOUSE_MOVE: 
                return mouseMove (evt, evt.x, evt.y); 
            case Event.MOUSE_DRAG: 
                return mouseDrag (evt, evt.x, evt.y); 
            case Event.MOUSE_ENTER: 
                return mouseEnter(evt, evt.x, evt.y); 
            case Event.MOUSE_EXIT: 
                return mouseExit (evt, evt.x, evt.y); 
            case Event.KEY_PRESS: 
            case Event.KEY_ACTION: 
                return keyDown  (evt, evt.key); 
            case Event.KEY_RELEASE: 
            case Event.KEY_ACTION_RELEASE: 
                return keyUp    (evt, evt.key); 
            case Event.GOT_FOCUS: 
                return gotFocus (evt, evt.arg); 
            case Event.LOST_FOCUS: 
                return lostFocus(evt, evt.arg); 
        } 
        return false; 
    } 
    // ... 
    public boolean action(Event evt, Object arg) 
    { 
        return false; 
    } 
    // ... 
}     
### 

 
W  metodzie  handleEvent  są  rozpoznawane  i  wywoływane  zdarzenia  związane  z  kliknięciami  przycisków 
(ACTION_EVENT), zdarzenia związane z myszką i z klawiaturą (np. MOUSE_DOWN KEY_PRESS) oraz 
zdarzenia związane z przemieszczeniem wyróżnienia sterownika (GOT_FOCUS LOST_FOCUS). 
 
Uwaga: W danej chwili może być wyróżniony tylko tylko jeden sterownik. Przemieszczenie wyróżnienia może 
być wykonane z programu, albo za pomocą klawiszy Tab i Shift-Tab
 
Po  rozpoznaniu  zdarzenia  jest  wywoływana  pomocnicza  metoda  obsługi  (np.  action,  mouseDown,  keyPress
gotFocus). Jej przedefiniowanie umożliwia dostarczenie własnej obsługi zdarzenia. 
 
 

public boolean action(Event evt, Object arg) 

Metoda  action  reaguje  na  akcję  wykonaną  na  sterowniku  (kliknięcie  przycisku,  wybranie  polecenia  menu, 
wybranie  elementu  listy,  naciśnięcie  klawisza  Enter  po  zakończeniu  edycji  klatki  tekstowej,  itp.),  w  tym  na 
akcję wykonaną za pomocą myszki i klawiatury. 
 
 

public boolean mouseDown (Event evt, int x, int y) 
public boolean mouseUp   (Event evt, int x, int y) 
public boolean mouseMove (Event evt, int x, int y) 
public boolean mouseDrag (Event evt, int x, int y) 
public boolean mouseEnter(Event evt, int x, int y) 
public boolean mouseExit (Event evt, int x, int y) 

background image

 

101 

Metody  reagują  na  akcję  wykonaną  za  pomocą  myszki.  Wykonanie  kliknięcia  jest  rozbite  na  dwie  podakcje: 
naciśnięcie  przycisku  myszki  (mouseDown)  oraz  zwolnienie  go  (mouseUp).  Ponadto  umożliwiono  obsługę 
przesuwania  i  przeciągania  kursora  myszki  (mouseMove,  mouseDrag)  oraz  obsługę  przemieszczenia  kursora 
myszki do wnętrza i na zewnątrz obszaru sterownika (mouseEntermouseExit). 
 
 

public boolean keyDown(Event evt, int key) 
public boolean keyUp  (Event evt, int key) 

Metody reagują na naciśnięcie klawisza (keyDown) oraz na zwolnienie go (keyUp). 
 
 

public boolean gotFocus (Event evt, Object arg) 
public boolean lostFocus(Event evt, Object arg) 

Metody reagują na otrzymanie/utratę wyróżnienia. 
 

public void requestFocus() 

Metoda przyznaje sterownikowi wyróżnienie. 
 
 
Następujący program, pokazany podczas wykonywania na ekranie Wyświetlanie znaków, wykreśla w punkcie 
kliknięcia ostatni znak wprowadzony z klawiatury. 
 
Ekran Wyświetlanie znaków 

### drawkey.gif 

 

import java.io.IOException; 
import java.applet.*; 
import java.awt.*; 
 
public  
class Master extends Frame { 
    Font font; 
    String chr = " "; 
    static Frame frame; 
    Master() 
    { 
        super("PressKey & Click"); 
        font = new Font("TimesRoman", Font.BOLD, 24); 
    } 
    public static void main(String args[]) 
        throws IOException 
    { 
        frame = new Master(); 
        frame.resize(160, 100); 
        frame.show(); 
        System.in.read(); 
        System.exit(0); 
    } 
    public boolean keyUp(Event evt, int key) 
    { 
        chr = String.valueOf((char)key); 
        return true; 
    } 
    public boolean mouseUp(Event evt, int x, int y) 
    { 
        Graphics gDC = getGraphics(); 
        gDC.setFont(font); 
        gDC.drawString(chr, x, y); 
        return true; 
    } 
    public boolean handleEvent(Event evt) 
    { 
        if(evt.id == Event.WINDOW_DESTROY) { 
            hide();                 // ukrycie ramki 
            dispose();              // zniszczenie ramki 
            return true; 
        } else  
            return super.handleEvent(evt); 

background image

 

102 

    } 

 
Zajście zdarzenia zewnętrznego powoduje wywołanie podanej metody handleEvent przedefiniowującej metodę 
domnieman
ą
 
W metodzie przedefiniowującej rozpoznaje się tylko zdarzenie WINDOW_DESTROY generowane przez System 
w chwili r
ęcznego zamknięcia ramki utworzonej przez aplikację
 
Pozostałe zdarzenia są obsługiwane przez predefiniowana metodę handleEvent wywoływaną w instrukcji 
 

return super.handleEvent(evt); 

 
Metody keyUp mouseUp są wywoływane przez tę właśnie metodę handleEvent
 
 
_________________________________________________________________________________________ 

Odtwarzanie pulpitu 

 
Podczas  programowania  w  środowisku  graficznym  należy  tak  definiować  metodę  paint,  aby  po  każdym 
wywołaniu jej przez System, nastąpiło pełne odtworzenie obsługiwanego przez nią pulpitu. 
 
Następujący  program  właściwie  dostosowano  do  warunków  obsługiwania  zdarzeń  w  dynamicznym  i 
wielookienkowym środowisku graficznym. 
 

import java.io.IOException; 
import java.applet.*; 
import java.awt.*; 
import java.util.*; 
 
public  
class Master extends Frame { 
    Font font; 
    String chr = " "; 
    Vector chars = new Vector(); 
    static Frame frame; 
    Master() 
    { 
        super("PressKey & Click"); 
        font = new Font("TimesRoman", Font.BOLD, 24); 
    } 
    public static void main(String args[]) 
        throws IOException 
    { 
        frame = new Master(); 
        frame.resize(160, 100); 
        frame.show(); 
        System.in.read(); 
        System.exit(0); 
    } 
    public boolean keyUp(Event evt, int key) 
    { 
        chr = String.valueOf((char)key); 
        return true; 
    } 
    public boolean mouseUp(Event evt, int x, int y) 
    { 
        Graphics gDC = getGraphics(); 
        gDC.setFont(font); 
        gDC.drawString(chr, x, y); 
        Item item = new Item(chr, x, y); 
        chars.addElement(item); 
        return true; 
    } 
    public void paint(Graphics gDC) 
    { 
        Font oldFont = gDC.getFont(); 

background image

 

103 

        gDC.setFont(font); 
        Enumeration charSet = chars.elements(); 
        while(charSet.hasMoreElements()) { 
            Object item = charSet.nextElement(); 
            ((Item)item).draw(gDC); 
        } 
        gDC.setFont(oldFont); 
    } 

 
class Item { 
    private String chr; 
    private int x, y; 
    public Item(String chr, int x, int y) 
    { 
        this.chr = chr; 
        this.x = x; 
        this.y = y; 
    } 
    public void draw(Graphics gDC) 
    { 
        gDC.drawString(chr, x, y); 
    } 

 
W programie użyto predefiniowanej klasy Vector implementującą kolekcję tablicową o nieograniczonej liczbie 
elementów oraz interfejsu Enumeration definiuj
ącym metody do iterowania kolekcji. 
 
Zgodnie z zasadami programowania obiektowego, każdy element przewidziany do wyświetlenia jest odrębnym 
obiektem (tu klasy Item). 
 
W klasie Item zdefiniowano nie tylko pola, ale również metodę do wykreślania obiektu. 
 
Rezultatem metody elements wywołanej w instrukcji 
 

Enumeration charSet = chars.elements(); 

 
jest  odnośnik  do  iteratora,  którego  klasa  zawiera  metody  hasMoreElements  i  nextElement.  Umożliwiają  one 
dokonanie przegl
ądu wszystkich elementów wstawionych do kolekcji chars. 
 
 
 

background image

 

104 

Część VI 

Programowanie współbieżne 

 
 
Istota  programowania  współbieżnego  polega  na  wykorzystaniu  możliwości  jednoczesnego  wykonania 
współpracujących i komunikujących się ze sobą procesów. W obrębie ustalonego programu takimi procesami 
są wątki
 
Wątkiem  jest  sekwencyjny  przepływ  sterowania  przez  kod  wynikowy  programu.  Program  jest    napisany 
wielowątkowo, jeśli podczas wykonywania go w środowisku wieloprocesorowym można stwierdzić, że są takie 
przedziały czasu, kiedy w co najmniej dwóch procesorach występuje współbieżny (tj. równoczesny) przepływ 
sterowania przez kod wynikowy programu. 
 
W  komputerach  jednoprocesorowych,  współbieżność  wątków  jest  tylko  emulowana,  a  w  każdej  chwili 
przepływ  sterowania  odbywa  się  w  tylko  jednym  wątku.  Ale  nawet  w  takim  środowisku,  program 
wielowątkowy powinien być napisany tak, jakby miał być wykonany przez wiele procesorów. 
 
Najprostszym  sposobem  utworzenia  programu  wielowątkowego  jest  utworzenie  obiektu  klasy  Thread
przekazanie  mu  odnośnika  do  obiektu  klasy  zawierającej  metodę  run,  a  następnie  wykonanie  na  rzecz 
pierwszego  z  tych  obiektów  metody  start.  Spowoduje  to  dla  danego  wątku  aktywowanie  metody  run.  Po  jej 
zakończeniu wątek przestanie istnieć. 
 
Następujący  program,  pokazany  podczas  wykonywania  na  ekranie  Pulsujące  okręgi,  ilustruje  zasadę 
programowania wielowątkowego. Tworzy on okna, które są obsługiwane przez niezależne wątki. 
 
Ekran Pulsujące okręgi 

### pulsars.gif 

 

import java.io.IOException; 
import java.awt.*; 
 
public  
class Master extends Frame implements Runnable { 
    static final int count = 5; 
    Thread thread; 
    Master(int frameNo) 
    { 
        super("Frame #" + frameNo); 
        resize(160, 100); 
        show(); 
        thread = new Thread(this);  // utworzenie obiektu 
        thread.start();             // utworzenie w

ą

tku 

    } 
    public static void main(String args[]) 
        throws IOException 
    { 
         Frame frame[] = new Frame[count]; 
         for(int i = 0; i < count ; i++) 
             frame[i] = new Master(i); 
         System.in.read(); 
         System.exit(0); 
    } 
    public void run() 
    { 
        int xC = size/2, yC = xC; 
        Graphics gDC = getGraphics(); 
        while(true) { 
            for(int i = 0; i < 70 ; i++) 
                gDC.drawOval(0, 0, i, i); 
            repaint(); 
        } 
    } 
    public boolean handleEvent(Event evt) 
    { 
        if(evt.id == Event.WINDOW_DESTROY) { 
            hide();                 // ukrycie okna 

background image

 

105 

            dispose();              // zniszczenie okna 
            thread.stop();          // zniszczenie w

ą

tku 

            return true; 
        } else  
            return super.handleEvent(evt); 
    } 

     
Utworzono count niezależnych wątków, z których każdy posługuje się odrębnym oknem. 
 
Kod wątków jest wspólny i określony przez metodę run.  
 
Zamknięcie okna powoduje zniszczenie posługującego się nim wątku. 
 
 
_________________________________________________________________________________________ 

Stany wątków 

 
Każdy  wątek  wymaga  istnienia  kontrolującego  go  obiektu  klasy  Thread.    Wykonanie  na  rzecz  obiektu 
kontrolującego metody start powoduje utworzenie wątku, a wykonanie metody stop jego zniszczenie.  
 
W  okresie  między  utworzeniem  i  zniszczeniem  wątek  istnieje,  ale  nie  musi  być  czynny.  Istniejący  wątek 
wykonuje się, śpijest zawieszony albo czeka
 
O  przebiegu  wykonania  wątku  decyduje  metoda  run  wywołana  niejawnie  przez  System  tuż  po  utworzeniu 
wątku. Metoda run należy do klasy implementującej interfejs Runnable. Miejsce jej wystąpienia jest określone 
podczas  tworzenia  obiektu  kontrolującego  wątek.  Zakończenie  wykonywania  metody  run  powoduje 
zniszczenie wątku. 
 
 

public Thread(Runnable runClass) 

Argument konstruktora identyfikuje obiekt klasy zawierającej definicję metody run
 

public void start() throws IllegalThreadStateException 

Wywołanie metody powoduje utworzenie wątku.  
 

public void stop() 

Wywołanie metody powoduje zniszczenie wątku. 
 

public static void sleep(long millis) 

Wywołanie metody powoduje uśpienie wątku na podany okres czasu, wyrażony w milisekundach. 
 

public void suspend() 

Wywołanie metody powoduje zawieszenie wątku. 
 

public void resume() 

Wywołanie metody powoduje odwieszenie wątku. 
 

public void wait() throws InterruptedException, 
                          IllegalMonitorStateException 

Wywołanie metody powoduje wstrzymanie wykonywania wątku z jego własnej inicjatywy. 
 

public void notify() throws IllegalMonitorStateException 

Wywołanie metody powoduje uwolnienie jednego dobrowolnie wstrzymanego wątku. 
 

public static void yield() 

Wywołanie  metody  powoduje  podzielenie  się  przez  wątek  dostępem  do  procesora.  Po  wywołaniu  tej  metody 
zostanie  podjęte  wykonywanie  innego  wątku  (o  ile  taki  istnieje).  Nie  wyklucza  to  jednak  zagłodzenia  wątku 
jeśli jest ich więcej niż dwa. 
 

public void join() throws InterruptedException 

background image

 

106 

Wywołanie metody powoduje powstrzymanie wykonywania wątku aż do zniszczenia go przez inny wątek. 
 

public boolean isAlive() 

Wywołanie  metody  umożliwia  stwierdzenie  czy  wątek  istnieje,  to  jest  czy  na  rzecz  obiektu  kontrolującego 
wątek wywołano już metodę start, ale nie wywołano jeszcze metody stop
 
 
_________________________________________________________________________________________ 

Priorytety 

 
Z każdym wątkiem jest związany priorytet, określający jego rangę wśród innych wątków. Priorytet jest liczbą z 
przedziału 1..10. Jeśli nie poda się go jawnie, to nowy wątek przejmie priorytet wątku który go utworzył. 
 
Jako zasadę przyjmuje się, że wątek o wyższym priorytecie uzyskuje większy przydział procesora niż wątek o 
niższym  priorytecie.  Nie  oznacza  to  jednak,  że  jeśli  są  wykonywane  wątki  o  różnych  priorytetach,  to  ten  o 
najwyższym zmonopolizuje procesor (chociaż może się tak stać!).  
 
Uwaga: Poleganie  na priorytetach wątków nie daje gwarancji co do kolejności ich wykonania i dlatego musi 
być uznane za przejaw złego stylu sterowania wykonaniem wątków. 
 
 
_________________________________________________________________________________________ 

Wątek główny 

 
W  chwili  rozpoczęcia  wykonywania  programu  istnieje  tylko  wątek  główny  należący  do  grupy  wątków  main
Wątek ten oraz każdy inny, może tworzyć inne wątki. 
 
Każdy  wątek  może  być  uczyniony  demonem.  Wykonywanie  programu  kończy  się  bezpośrednio  po  tym,  gdy 
zakończy się wykonywanie ostatniego wątku, który nie jest demonem.  
 
Przykład Wątek główny 
 

import java.io.IOException; 
 
public 
class Master { 
    public static void main(String args[]) 
        throws IOException 
    { 
        Thread thread = Thread.currentThread(); 
        System.out.println(thread); 
        System.in.read(); 
    } 

 
Wykonanie programu powoduje wyprowadzenie napisu 
 

 

Thread[main,5,main] 

 
 
_________________________________________________________________________________________ 

Tworzenie wątków 

 
W  celu  utworzenia  wątku  należy  utworzyć  kontrolujący  go  obiekt  klasy  Thread.  Obiektowi  temu  należy 
przekazać odnośnik do obiektu klasy implementującej metodę run interfejsu Runnable.  
 
Uwaga: Metodę run można zadeklarować w tej samej klasie, do której należy metoda tworząca wątek. Klasa 
tej metody musi implementować interfejs Runnable
 

import java.io.IOException; 

background image

 

107 

 
public 
class Master implements Runnable { 
    public static void main(String args[]) 
        throws IOException 
    { 
        System.out.println("Hello from MainThread"); 
        Master myThis = new Master(); 
        new Thread(myThis, "OtherThread").start(); 
        System.in.read(); 
    } 
    public void run() 
    { 
        System.out.println("Hello from OtherThread"); 
    } 

 
Podany program napisano wielowątkowo. Jest on wykonywany dwuwątkowo od chwili wywołania metody start 
 

new Thread(this, "OtherThread").start(); 

 
to jest do chwili zakończenia wykonywania funkcji main albo metody run (co zajdzie wcześniej). 
 
Wykonanie programu powoduje wyprowadzenie napisu 
 

 

Hello from MainThread 

 

Hello from OtherThread 

 
 
_________________________________________________________________________________________ 

Synchronizowanie wątków 

 
Jeśli  dwa,  lub  więcej,  wątków  dzieli  wspólny  zasób  (na  przykład  pamięć  operacyjną),  to  szczególną  troską 
należy otoczyć dostęp do tego zasobu.  
 
Aby się przekonać o konieczności synchronizowania dostępu, wystarczy rozpatrzyć sytuację, gdy rolę wątków 
pełnią dwaj kasjerzy, którzy mają nie-synchronizowany  dostęp do wspólnej bazy danych.  
 
Jeśli na wspólnym koncie dwóch osób jest na przykład $100, a każda z nich wpłaca w osobnym okienku $20, to 
może zaistnieć następująca sytuacja 
 
Pierwszy kasjer sprawdza konto i odnotowuje jego stan ($100), ale coś odrywa go do telefonu.  
 
Drugi kasjer sprawdza konto, odnotowuje jego stan ($100), dodaje $20 i aktualizuje konto (do $120). 
 
Pierwszy kasjer ko
ńczy rozmowę, dodaje $20 do odnotowanej sumy i aktualizuje bazę (do $120). 
 
W  nast
ępstwie  nie-synchronizowanego  dostępu  do  bazy  danych,  następuje  zwiększenie  konta  nie  o  $40,  ale  o 
$20. 
 
Następujący  program  ilustruje  sposób  rozwiązania  przedstawionego  problemu.  Dzięki  synchronizacji  dostępu 
do  bazy  dataBase,  funkcja  updateSavings  może  być  w  danej  chwili  wykonywana  tylko  przez  co  najwyżej 
jeden wątek. 
 

import java.io.IOException; 
 
class Savings { 
    private float savings; 
    // ... 
    void add(float amount) 
    { 
        savings += amount; 
    } 

background image

 

108 

 
class Transaction { 
    int account; 
    float amount; 
    Transaction(int account, int amount) 
    { 
        // ... 
    } 
    // ... 

 
public 
class Master { 
    static float savingsData[] =  
                     { 100, 500, 300, 400, 200 }; 
    static Savings dataBase[] =  
                       new Savings[savingsData.length]; 
    static int noOfTellers = 2; 
    public static void main(String args[]) 
        throws IOException, InterruptedException 
    { 
        loadDataBase(dataBase, savingsData); 
        Transaction  
            setOne[] = {  
                           new Transaction(2, 10),  
                           new Transaction(4, 20) }, 
            setTwo[] = {  
                           new Transaction(0, 30),  
                           new Transaction(2, 10) 
                       }; 
 
                       //  =================== 
                       //  100 500 300 400 200  Baza danych 
                       //           10      20  John 
                       //   30      10          Bill 
                       //  =================== 
                       //  130 500 320 400 220  results 
 
        Teller john = new Teller("John", setOne, dataBase), 
               bill = new Teller("Bill", setTwo, dataBase); 
 
        Thread tellerOne = new Thread(john), 
               tellerTwo = new Thread(bill); 
 
        showDataBase(dataBase); 
        tellerOne.start(); 
        tellerTwo.start(); 
            // ... 
            Thread.currentThread().sleep(1000); 
            // ... 
        showDataBase(dataBase); 
        System.in.read(); 
    } 
    // ... 
    static void loadDataBase(Savings dataBase[],  
                             float savings[]) 
    { 
        // ... 
    } 
    static void showDataBase(Savings dataBase[]) 
    { 
        // ... 
    } 

 
class Teller implements Runnable { 
    private String name; 
    private Transaction set[]; 
    private Savings dataBase[]; 
    Teller(String name, Transaction set[],  
           Savings dataBase[]) 
    { 
        this.name = name; 

background image

 

109 

        this.set = set; 
        this.dataBase = dataBase; 
    } 
    private void updateSavings(int i) 
    { 
        int account = set[i].account; 
        float amount = set[i].amount; 
        System.out.println(name + " " +  
                           account + " " + amount); 
        dataBase[account].add(amount); 
    } 
    public void run() 
    { 
        for(int i = 0; i < set.length ; i++) 
            synchronized(dataBase) 
                updateSavings(i); 
        Master.setDone(); 
    } 

 
Podany program wyprowadza napis pokazany w tabeli Transakcje. Kolejność wierszy środkowej części napisu 
może się zmieniać od wykonania do wykonania. Uogólnienie programu na dowolną liczbę kasjerów i transakcji 
jest trywialne. 
 
Tabela Transakcje 

### 
 

Account #1 $100 

 

Account #1 $500 

 

Account #2 $300 

 

Account #3 $400 

 

Account #4 $200 

 
 

John 2 10 

 

Bill 0 30 

 

John 4 20 

 

Bill 2 10 

 
 

Account #1 $130 

 

Account #1 $500 

 

Account #2 $320 

 

Account #3 $400 

 

Account #4 $220 

### 

 
 
_________________________________________________________________________________________ 

Instrukcja synchronizująca 

 
Do synchronizowania wątków służy instrukcja 
 

 

synchronized(exp)Block 

 
w której exp jest nazwą odnośnika, a Block jest instrukcją grupującą. 
 
Wykonanie  instrukcji  synchronizującej  powoduje  przydzielenie  wątkowi  bloku  sekcji  krytycznej  związanej  z 
obiektem identyfikowanym przez odnośnik, a po wykonaniu instrukcji bloku, zwolnienie sekcji. 
 
Uwaga: Jeśli podczas wykonywania sekcji krytycznej związanej z pewnym obiektem, jakikolwiek inny wątek 
podejmie próbę wykonania tej samej sekcji krytycznej związanej z tym samym obiektem, to jego wykonanie 
wątku zostanie zablokowane do chwili zwolnienia sekcji. 
 
W następującej funkcji, wykonanie sekcji krytycznej wyznaczonej przez instrukcję synchronized, może być w 
danej  chwili,  na  rzecz  tego  samego  obiektu  point,  realizowane  tylko  przez  co  najwyżej  jeden  wątek.  Dzięki 
temu wywołanie metody incPoint powoduje zwiększenie obu pól (xy) o tę samą wartość. 
 

class Master { 

background image

 

110 

    // ... 
    public Point incPoint(Point point) 
    { 
        synchronized(point) { 
            point.incXY(); 
            return point; 
        } 
    } 
}  
 
class Point { 
    int x, y; 
    void incXY() 
    { 
        this.x = x + 1; 
        this.y = y + 1; 
    } 
    // ... 

 
 
Uwaga:  Gdyby  zrezygnowano  z  użycia  instrukcji  synchronized,  a  metodę  incPoint  wywołano  z  dwóch 
różnych wątków, to mogłoby się zdarzyć, że po takich operacjach współrzędne x i y punktu nie byłyby równe.  
 
 
_________________________________________________________________________________________ 

Procedury synchronizowane 

 
Procedurą synchronizowaną jest procedura zadeklarowana ze specyfikatorem synchronized
 

class Counter { 
    private int counter = 0; 
    public synchronized void Counter() 
    { 
        counter += 1; 
    } 

 
Uwaga:  Semantyka  procedur  synchronizowanych  zostanie  wyjaśniona  przez  powołanie  się  na  instrukcję 
synchronized 
 
 

Metody synchronizowane 

 
Wywołanie w instrukcji 
 

 

ref.met(argarg, ... , arg); 

 
metody  synchronizowanej  met,  na  rzecz  obiektu  identyfikowanego  przez  odnośnik  ref  jest  równoważne 
wywołaniu  
 

 

synchronized(ref) { 

 

    ref.met2(argarg, ... , arg); 

 

 
identycznej z nią metody niesynchronizowanej met2
 
 

Funkcje synchronizowane 

 
Wywołanie w instrukcji 
 

 

Class.fun(argarg, ... , arg); 

background image

 

111 

 

 
funkcji synchronizowanej fun należącej do klasy Class jest równoważne wywołaniu  
 

 

try { 

 

    synchronized(Class.forName(Class)) { 

              Class.fun2(argarg, ... , arg); 
 
 

    } 

 

 

catch(ClassNotFoundException e) { 

 

 
identycznej z nią funkcji niesynchronizowanej fun2
 
 
_________________________________________________________________________________________ 

Monitor 

 
Z każdym obiektem klasy jest skojarzony monitor. Jeśli metoda synchronizowana klasy zostanie wywołana na 
rzecz obiektu, to wywołujący ją wątek zajmie monitor tego obiektu.  
 
Do czasu zwolnienia monitora zostanie zablokowane wykonanie każdego innego wątku, który podejmie próbę 
wywołania (na rzecz tego samego obiektu), dowolnej funkcji sychronizowanej danej klasy. 
 
Wątek  zajmujący  monitor  może  posługiwać  się  metodami  wait  i  notify.  Biorąc  pod  uwagę  pojęcie  monitora, 
ich opisy przybierają następującą postać: 
 
 

public void wait() throws InterruptedException, 
                          IllegalMonitorStateException 

Wywołanie  metody  powoduje  zwolnienie  monitora  przez  wątek  i  dobrowolne  wstrzymanie  wykonywania 
wątku. Umożliwi to innemu wątkowi ubieganie się o przydzielenie monitora.  
 

public void notify() throws IllegalMonitorStateException 

Wywołanie  metody  powoduje  uwolnienie  jednego  dobrowolnie  wstrzymanego  wątku.  Umożliwi  to 
uwolnionemu  wątkowi  ubieganie  się  o  zajęcie  monitora,  ale  nie  wcześniej  niż  monitor  zostanie  zwolniony 
przez wątek wywołujący metodę notify
 
 

Produkcja i konsumpcja 

 
Następujący  program  ilustruje  użycie  monitora  do  oprogramowana  klasycznego  problemu  Producent-
Konsument
.  Producent  wkłada  liczby  do  pudełka,  a  konsument  je  wyjmuje.  Jeśli  pudełko  jest  puste,  to  czeka 
konsument, a jeśli jest pełne, to czeka producent. 
 
Przykład Produkcja-Konsumpcja 

 
import java.io.IOException; 
import java.io.*; 
 
public  
class Master { 
    static Box box = new Box(); 
    public static void main(String args[]) 
        throws IOException 
    { 
        new Producer(box); 
        new Consumer(box); 
    } 

 
class Producer implements Runnable { 

background image

 

112 

    Box box; 
    Producer(Box box) 
    { 
        this.box = box; 
        new Thread(this).start(); 
    } 
    public void run() 
    { 
        for(int number = 0; number < 5 ; number++) 
            box.putInto(number+1); 
        box.setDone(); 
    } 

 
class Consumer implements Runnable { 
    Box box; 
    Consumer(Box box) 
    { 
        this.box = box; 
        new Thread(this).start(); 
    } 
    public void run() 
    { 
        int gotFrom; 
        while(true)  
            try { 
                gotFrom = box.getFrom(); 
            } 
            catch(IOException e) { 
            } 
    } 

 
class Box { 
    int contents; 
    boolean boxEmpty = true;    
    static boolean producerDone = false;  // flaga ko

ń

ca 

    public Box() 
    { 
    } 
    synchronized void setDone() 
    { 
        producerDone = true; 
    } 
    synchronized int getFrom() 
    { 
        if(boxEmpty) { 
            if(producerDone) { 
                System.in.read(); 
                System.exit(0); 
            } else 
                try { 
                    wait(); 
                } 
                catch(InterruptedException e) { 
                } 
        } 
        int number = contents; 
        System.out.println("Number " + number +  
                           " from Box"); 
        boxEmpty = true; 
        notify(); 
        return number; 
    } 
    synchronized void putInto(int number) 
    { 
        if(!boxEmpty) 
            try { 
                wait(); 
            } 
            catch(InterruptedException e) { 
            } 
        contents = number; 

background image

 

113 

        System.out.println("Number " + number +  
                           " to Box"); 
        boxEmpty = false; 
        notify(); 
    } 

 
Producent i konsument operują na wspólnym zasobie jakim jest pudełko box
 
Wykonanie programu powoduje wyprowadzenie napisu 
 

 

Number 1 to Box 

 

Number 1 from Box 

 

Number 2 to Box 

 

Number 2 from Box 

 

Number 3 to Box 

itd. 
 
Po  wstawieniu  do  programu  bardziej  szczegółowych  poleceń  informujących  o  przebiegu  wydarzeń  można 
otrzyma
ć sekwencję napisów pokazaną w tabeli Produkcja-konsumpcja
 
Tabela Produkcja-konsumpcja 

### 
 

koniec wykonania funkcji main 

ale wykonanie trwa 

 

startuje producent 

zaczyna działa

ć

  

 

producent wywołuje putInto(1) 

 

producent wstawia 1 w pudełka 

 

producent wywołuje putInto(2) 

 

startuje konsument 

dopiero teraz  

 

konsument wywołuje getFrom 

 

konsument dostaje 1 z pudełka 

 

konsument wywołuje getFrom 

 

producent wstawia 2 do pudełka 

 

konsument dostaje 2 z pudełka 

 

producent wywołuje putInto(3) 

 

konsument wywołuje getFrom 

 

producent wstawia 3 do pudełka 

 

producent wywołuje putInto(4) 

 

konsument dostaje 3 z pudełka 

 

producent wstawia 4 do pudełka 

 

konsument wywołuje getFrom 

 

producent wywołuje putInto(5) 

 

konsument dostaje 4 z pudełka 

 

producent wstawia 5 do pudełka 

 

konsument wywołuje getFrom 

 

producent ko

ń

czy wykonywanie 

ustawia flag

ę

 ko

ń

ca 

 

konsument dostaje 5 z pudełka 

 

konsument wywołuje getFrom 

 

 

konsument ko

ń

czy wykonywanie 

po zbadaniu flagi 

### 

 
 
_________________________________________________________________________________________ 

Impas 

 
O ile synchronizacja nie jest zaprojektowana prawidłowo, może powstać impas. Występuje on wówczas, gdy w 
zestawie  współdziałających  wątków  każdy  jest  zawieszony  albo  dobrowolnie  wstrzymany,  ale  nie  istnieje 
możliwość odwieszenia ani uwolnienia przynajmniej jednego wątku. 
 
Następujący program monitoruje pracę dwóch osób. Każda z nich informuje o postępach swoich i konkurenta.  
 

import java.io.IOException; 
 
public 
class Master { 
    static Worker tom, bob; 
    public static void main(String args[]) 

Komentarz [JB1]:  

background image

 

114 

        throws IOException 
    { 
        tom = new Worker("Tom"); 
        bob = new Worker("Bob"); 
        new Thread(tom.setOther(bob)).start(); 
        new Thread(bob.setOther(tom)).start(); 
        System.in.read(); 
    } 
    static synchronized void sendMessage(String string) 
    { 
        System.out.println(string); 
    } 

 
class Worker implements Runnable { 
    private long counter = 0; 
    String name; 
    Worker other; 
    Worker(String name) 
    { 
        this.name = name; 
    } 
    Worker setOther(Worker other) 
    { 
        this.other = other; 
        return this; 
    } 
    synchronized void showStatus() 
    { 
        Master.sendMessage(name + " " + counter + 
                           ", other: " + other.peep()); 
    } 
    synchronized long peep() 
    { 
        return counter; 
    } 
    public void run() 
    { 
        while(true) { 
            counter++;          // wykonanie pracy 
            showStatus();       // komunikat 
        } 
    } 

 
Podczas  wykonywania  programu  może  (ale  nie  musi!)  dojść  do  impasu.  Dojdzie  do  niego  na  przykład  przy 
następującym splocie wydarzeń: 
 
1.  Wątek Tom wywołuje metodę showStatus
2.  Ponieważ showStatus jest metodą synchronizowaną, więc wątkowi Tom przydziela się monitor związany 

ze zmienną Master.tom

 
 

Niech w tym miejscu zostanie przerwane wykonywanie wątku Tom

 
3.  Zaczyna się wykonywać wątek Bob i wywołuje metodę showStatus
4.  Ponieważ  showStatus  jest  metodą  synchronizowaną,  więc  wątkowi Bob przydziela  się monitor związany 

ze zmienną Master.bob

5.  Metoda showStatus wywołuje metodę synchronizowaną peep
6.  Ponieważ  peep  jest  metodą  synchronizowaną,  więc  wątek  Bob  spodziewa  się  przydzielenia  monitora 

związanego  ze  zmienną  Master.tom  (zmienna  other  jest  odnośnikiem  do  tej  właśnie  zmiennej).  Monitor 
jest już jednak przydzielony, więc wątek Bob zawiesza się, w oczekiwaniu na zwolnienie monitora. 

 
 

Niech w tym miejscu nastąpi wznowienie wykonywania wątku Tom

 
7.  Wątek Tom wywołuje metodę peep
8.  Ponieważ  peep  jest  metodą  synchronizowaną,  więc  wątek  Tom  spodziewa  się  przydzielenia  monitora 

związanego ze zmienną Master.bob (zmienna other jest odnośnikiem do tej właśnie zmiennej).  

background image

 

115 

9.  Monitor  jest  już  jednak  przydzielony,  więc  wątek  Tom  zawiesza  się,  w  oczekiwaniu  na  zwolnienie 

monitora. 

 
Wystąpił impas. Żaden z rozpatrywanych wątków nie wykonuje się.  
 
Zgodnie z oczekiwaniami, wykonanie programu w systemie Windows 95 dość szybko doprowadzało do impasu. 
Niemal  każde  wykonanie  dawało  inną  sekwencję  wyjściową.  Najkrótszą  z  uzyskanych,  ale  dłuższą  od  pustej 
(sic!), przytoczono w tabeli Wyniki przed impasem. Najbardziej interesujący jest niej drugi wiersz, z którego 
wynika, że Tom wykonał już 2 operacje, podczas gdy informował o wykonaniu tylko jednej! 
 
Tabela Wyniki przed impasem 

### 
 

Tom 1, other: 0 

 

Bob 1, other: 2 

 

Tom 2, other: 1 

 

Tom 3, other: 1  

 

Tom 4, other: 1 

 

Bob 2, other: 4 

 

Tom 5, other: 3 

### 

background image

 

116 

Część VII 

Programowanie apletów 

 
W  odróżnieniu  od  aplikacji,  która  jest  programem  samodzielnym,  aplet  jest  programem  wbudowanym  w  inną 
aplikację (np. w przeglądarkę Netscape). Aplikacja ta zapewnia wykonanie i wizualizację apletu. 
 
Wraz z B-kodem apletu należy dostarczyć przeglądarce opis apletu. Opis jest zawarty w pliku z rozszerzeniem 
.html. Jego nazwa jest zazwyczaj identyczna z nazwą pliku zawierającego program źródłowy apletu.  
 
Następujący aplet, pokazany podczas wykonania na ekranie Wachlarz, wyświetla obracający się odcinek. 
 
Ekran Wachlarz 

### fan.gif 

 
Przykład Wachlarz 
 

<applet code=Master.class width=0 height=0> 
</applet> 
=========================================== 
 
import java.applet.*; 
import java.awt.*; 
 
public  
class Master extends Applet implements Runnable { 
    private boolean stopped = false; 
    private final int rad = 50; 
    private Graphics gDC; 
    private Thread thread = new Thread(this); 
    private int size, xC, yC; 
    public void init() 
    { 
        gDC = getGraphics(); 
        size = 2*rad + 10; 
        xC = yC = size/2; 
        resize(size, size); 
        thread.start(); 
    } 
    public boolean mouseUp(Event evt, int x, int y) 
    { 
        stopped = !stopped; 
        return true; 
    } 
    public void run() 
    { 
        double angle = 0; 
        int i = 0; 
        while(!stopped) { 
            if(i++ == 40) { 
                repaint(); 
                i = 0; 
                angle = 0; 
            } 
            int x = (int)(rad * Math.cos(angle)), 
                y = (int)(rad * Math.sin(angle)); 
            gDC.drawLine(xC + x, yC - y, yC - x, yC + y); 
            try { 
                Thread.currentThread().sleep(100); 
            } 
            catch(InterruptedException e) { 
                thread.stop(); 
            } 
            angle = angle + Math.PI / 36; 
        } 
    } 

 
Rozmiary apletu określono nie w jego opisie, ale w w metodzie init programu. 
 

background image

 

117 

Kliknięcie lewym przyciskiem myszki powoduje zakończenie wykreślania. 
 
 
_________________________________________________________________________________________ 

Opis 

 
Z  każdym  apletem  przewidzianym  do  wykonania  w  przeglądarce  musi  być  związany  dokument  HTML 
zawierający opis apletu.  
 
Opis apletu musi zawierać parametry codewidth i height. Argumenty zawarte we frazach param są dostępne 
w programie za pomocą metody getParameter
 
Składnię opisu apletu zamieszczono w tabeli Opis apletu. Wymieniony w niej TekstZastępczy jest wyświetlany 
przez przeglądarkę, która nie rozpoznaje opisu apletu. 
 
Tabela Opis apletu 

### 
 

<applet 

 

codebase = 

Ś

cie

ż

ka 

 

code = Plik 

 

alt = Tekst 

 

name = Oznaczenie 

 

width = Szeroko

ść

 

 

height = Wysoko

ść

 

 

align = Wyrównanie 

 

vspace = Uskok 

 

hspace = Wci

ę

cie 

 

 

<param name = Nazwa value = Warto

ść

 

 

<param name = Nazwa value = Warto

ść

 

 

... j.w. 

 

TekstZast

ę

pczy 

 

</applet> 

### 

 

codebase = 

Ś

cie

ż

ka 

Parametr określa ścieżkę do katalogu zawierającego plik z B-kodem apletu. Ścieżka względna, np. subdir\code 
jest odnoszona do katalogu, w którym znajduje się opis apletu. Ścieżką domyślną jest . (kropka). 
 

code = Plik 

Parametr  podaje  nazwę  pliku  z  rozszerzeniem  .class,  zawierającego  B-kod  apletu.  Przyjmuje  się,  że  nazwę 
ś

cieżki do pliku określa parametr codebase

 

alt = Tekst 

Parametr  podaje  tekst,  który  zostanie  wyświetlony  zamiast  apletu  przez  taką  przeglądarkę,  która  rozpoznaje 
opis apletu, ale nie może wyświetlić apletu. 
 

name = Oznaczenie 

Parametr podaje nazwę apletu, odróżniającą go od innych apletów tej samej strony WWW. W celu odwołania się 
do apletu przez nazwę należy w programie użyć instrukcji podobnej do  
 

Applet myApplet = getAppletContext().getApplet(Oznaczenie); 

 

width = Szeroko

ść

 

Parametr podaje początkową szerokość apletu na stronie WWW (wyrażoną w pikselach). 
 

height = Wysoko

ść

 

Parametr podaje początkową wysokość apletu na stronie WWW (wyrażoną w pikselach). 
 

align = Wyrównanie 

Parametr  podaje  sposób  usytuowania  apletu  na  stronie  WWW.  Dopuszczalnymi  wartościami  Wyrównania  są: 
leftrighttoptexttopmiddleabsmiddlebaselinebottomabsbottom
 

background image

 

118 

vspace = Uskok 

Parametr podaje pionowy odstęp powyżej i poniżej apletu (wyrażony w pikselach). 
 

hspace = Wci

ę

cie 

Parametr podaje poziomy odstęp przed i po aplecie (wyrażony w pikselach). 
 

<param name = Nazwa value = Warto

ść

 > 

Parametr  umożliwia  określenie  parametru  apletu.  Parametrów  param  może  być  w  opisie  apletu  dowolnie 
wiele
. Każdy z nich określa Nazwę i Wartość argumentu (małe i duże litery są uznawana za różne). Wartości
które zawierają odstępy i znaki specjalne, należy ująć w cudzysłowy
 
W celu użycia w programie parametru apletu należy użyć instrukcji 
 

String Value = getParameter("Name"); 

 
a następnie dokonać analizy i konwersji łańcucha Value, na przykład 
 

int Fixed = Integer.parseInt(Value); 

 
albo 
 

double Real = Double.valueOf(Value).doubleValue(); 

 
Uwaga: Jeśli oczekiwany argument nie istnieje, to metoda getParameter zwraca odniesienie puste (null). 
 
Następujący aplet, pokazany podczas wykonania na ekranie Kolory, wyświetla napis  
 

 

Hello 

 
czcionką o rozmiarze 48 punktów, w kolorze karmazynowym
 
Uwaga: Kolor karmazynowy ma składniki RGB(255,0,255)
 
Ekran Kolory 

### colors.gif 

 

<applet 
    codebase = d:\applets 
    code     = Master.class 
    width    = 300 
    height   =  80> 
<param name  = Size value = 48> 
<param name  = Color value = "255,0,255"> 
</applet> 
========================================= 

 

import java.applet.*; 
import java.awt.*; 
 
public  
class Master extends Applet { 
    private static Color color; 
    private static Font font; 
    public void init() 
    { 
        String sizeString = getParameter("Size"); 
        if(sizeString == null) 
            throw new IllegalArgumentException("No Size"); 
        int size; 
        try { 
            size = Integer.parseInt(sizeString); 
        } 
        catch(NumberFormatException e) { 
            throw new IllegalArgumentException("Size"); 
        } 

background image

 

119 

        String colorString = getParameter("Color"); 
        if(colorString == null) 
            throw new IllegalArgumentException("No Color"); 
        color = getColor(colorString); 
        font = new Font("Courier", Font.ITALIC, size); 
    } 
    public void paint(Graphics gDC) 
    { 
        gDC.setColor(color); 
        gDC.setFont(font); 
        gDC.drawString("Hello", 100, 60); 
    } 
    Color getColor(String triplet) 
    { 
        String string = triplet + ",.", tmpString; 
        int rgb[] = new int [3]; 
        for(int i = 0; i < 3 ; i++) { 
            int index = string.indexOf(","); 
            if(index < 0) 
                throw new IllegalArgumentException("Color"); 
            tmpString = string.substring(0, index); 
            try { 
                rgb[i] = Integer.parseInt(tmpString); 
            } 
            catch(NumberFormatException e) { 
                throw new IllegalArgumentException("Color"); 
            } 
            string = string.substring(index+1); 
        } 
        if(!string.equals(".")) 
            throw new IllegalArgumentException("Color"); 
        return new Color(rgb[0], rgb[1], rgb[2]);     
    } 

 
Dokonano pełnej kontroli poprawności parametrów apletu. Zabieg taki jest kosztowny, ale mimo to zalecany
 
 
_________________________________________________________________________________________ 

Wykonanie 

 
W  chwili  rozpoznania  opisu  apletu,  przeglądarka  tworzy  obiekt  klasy  apletu,  a  następnie  wywołuje  na  jego 
rzecz metodę init. Tuż przed zniszczeniem obiektu jest wywoływana metoda destroy.  
 
W  czasie  istnienia  obiektu  są  dodatkowo  wywoływane  metody  start,  stop,  update  i  paint.  Z  dowolnej 
procedury apletu mogą być ponadto wywoływane metody repaintresize reshape
 
 

public void init() 

Metoda init jest wywoływana jednokrotnie,  tuż po pierwszym załadowaniu strony WWW zawierającej aplet. 
Przejście  na  inną  stronę  WWW  i  powrót  do  strony  zawierającej  aplet  nie  powoduje  ponownego  wywołania 
metody init
 

public void start() 

Metoda start jest wywoływana za każdym razem, gdy strona WWW zawierająca aplet staje się stroną bieżącą. 
 

public void stop() 

Metoda  stop  jest  wywoływana  za  każdym  razem  gdy  strona  WWW  zawierająca  aplet  przestaje  być  stroną 
bieżącą. 
 

public void update(Graphics gDC) 

Metoda  update  jest  wywoływana za każdym razem, gdy zachodzi potrzeba wykreślenia apletu. Na przykład 
tuż  po  wykonaniu  metody  start  oraz  po  wywołaniu  takich  metod  jak  repaint  i  resize.  Domniemana  metoda 
update czyści pulpit apletu aktualnym kolorem tła, a następnie wywołuje metodę paint
 

background image

 

120 

public void paint(Graphics gDC) 

Wywołanie  metody  paint  służy  do  wykreślenia  apletu.  Każdy  aplet,  który  trwale  modyfikuje  swój  pulpit 
powinien dostarczyć własną definicję metody paint. Domniemana metoda paint nie robi nic. Kilka kolejnych 
wywołań metody paint może być zastąpione przez System tylko jednym jej wykonaniem. 
 

public void repaint() 
public void repaint(long time) 
public void repaint(int x, int y, int width, int height) 

Wywołanie  metody  repaint  jest  apelem,  aby  System  w  miarę  szybko,  ale  w  wybranej  przez  siebie  chwili, 
wywołał metodę update (która z kolei wywoła paint). O ile nie przedefiniowano tych metod, doprowadzi to do 
wyczyszczenia  i  odtworzenia  pulpitu.  Metoda  repaint  może  być  wywołana  z  argumentami  określającymi 
prostokątny  obszar  obcinania  ograniczający  obszar  odtwarzania  pulpitu.  Jeśli  zostanie  wywołana  z 
argumentem time, a System nie zdoła uwzględnić apelu w podanym czasie, to apel ma być zignorowany
 

public void resize(int width, int height) 

Wywołanie  metody  resize  powoduje  zmianę  rozmiarów  apletu.  Bezpośrednio  po  uwzględnieniu  nowych 
rozmiarów, System wywołuje metodę repaint
 

public void destroy() 

Metoda destroy jest wywoływana jednokrotnie, tuż przed jawnym zniszczeniem apletu (na przykład w chwili 
zamknięcia  przeglądarki).  Metoda  destroy  jest  zazwyczaj  wykorzystywana  do  zwolnienia  zasobów 
przydzielonych apletowi. 
 
 
Następujący  aplet,  pokazany  podczas  wykonania  na  ekranie  Obsługa  apletu,  wyświetla  w  odrębnym  oknie 
informacje o wywoływanych zewnętrznie metodach jego klasy. 
 
Ekran Obsługa apletu 

### methods.gif 

 

<applet code=Master.class width=160 height=80> 
</applet> 
============================================= 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    Frame frame; 
    int yPos = 20; 
    Graphics gDC; 
    public void init() 
    { 
        frame = new MyFrame("Msg"); 
        frame.resize(100, 200); 
        frame.show(); 
        gDC = frame.getGraphics(); 
        sendMsg("init"); 
    } 
    void sendMsg(String msg) 
    { 
        gDC.drawString(msg, 10, yPos); 
        yPos = yPos + 20; 
    } 
    public void start() 
    { 
        sendMsg("start"); 
    } 
    public void stop() 
    { 
        sendMsg("stop"); 
    } 
    public void update(Graphics gDC) 
    { 
        sendMsg("update"); 
        super.update(gDC); 

background image

 

121 

    } 
    public void paint(Graphics gDC) 
    { 
        gDC.drawString("Click me!", 10, 20); 
        sendMsg("paint"); 
    } 
    public boolean mouseUp(Event evt, int x, int y) 
    { 
        sendMsg("mouseUp"); 
        return true; 
    } 

 
class MyFrame extends Frame { 
    MyFrame(String caption) 
    { 
        super(caption); 
    } 
    public void update(Graphics gDC) 
    { 
    } 

 
Gdyby instrukcję  
 

frame = new MyFrame("Msg"); 

 
zastąpiono instrukcją 
 

frame = new Frame("Msg"); 

 
to  po  wywołaniu  predefiniowanej  metody  update  klasy  Frame  (co  nastąpiłoby  po  wykonaniu  metody  start), 
nast
ąpiłoby wyczyszczenie napisu 
 

 

init 

 
już wysłanego do okna. 
 
Wynika stąd wniosek, że posługując się odrębnymi oknami tworzonymi przez aplet, należy unikać wykreślania w 
nich tekstów i grafiki podczas wykonywania metod init start
 
 
_________________________________________________________________________________________ 

Otoczenie 

 
Każdy  aplet  ma  otoczenie,  które  może  rozpoznać  i  z  którym  może  się  komunikować.    Podstawowymi 
metodami  do  rozpoznania  otoczenia  apletu  są  getCodeBase  i  getDocumentBase.  Wysyłanie  krótkich 
komunikatów do otoczenia odbywa się za pomocą metody showStatus
 
 

Lokalizator 

 
Lokalizowanie  zasobów  otoczenia  odbywa  się  za  pomocą  lokalizatora  zasobów  (Uniform  Resource  Locator). 
W  ogólnym  wypadku,  parametrami  lokalizatora  są:  nazwa  protokołu  komunikacyjnego,  najczęściej  HTTP 
(Hypertext Transfer Protocol),  nazwa komputera-gospodarza (host),  numer portu (dla HTTP zwyczajowo 80
oraz nazwa pliku.  
 
Informacje o lokalizatorze są przechowywane w obiekcie klasy URL (Uniform Resource Locator) inicjowanym 
na przykład za pomocą argumentu 
 

 

http://www.microsoft.com:80/index.html 

 
 

background image

 

122 

public URL getCodeBase() 

Metoda  dostarcza  odnośnik  do  obiektu  klasy  URL  identyfikującego  katalog,  w  którym  znajduje  się  B-kod 
apletu. 
 

public URL getDocumentBase() 

Metoda  dostarcza  odnośnik  do obiektu  klasy URL identyfikującego  katalog, w którym znajduje się dokument 
HTML z opisem apletu. 
 

public void showStatus(String msg) 

Wywołanie metody powoduje wyświetlenie komunikatu o stanie apletu. 
 
 
Uwaga: Ujawnienie okna konsoli w przeglądarce Netscape wymaga wydania w niej polecenia Options/Show 
Java Console
.  
 
Następujący aplet, pokazany podczas wykonania na ekranie Pęcherzyki, wyświetla obraz zawarty w pliku. Plik 
znajduje się w tym samym katalogu co B-kod apletu. 
 
Ekran Pęcherzyki 

### bubbles.gif 

 

<applet code=Master.class width=160 height=100> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
import java.net.*; 
 
public  
class Master extends Applet { 
    Image myImage; 
    public void init() 
    { 
        URL codeBase = getCodeBase(); 
        myImage = getImage(codeBase, "Bubbles.gif"); 
    } 
    public void paint(Graphics gDC) 
    { 
        gDC.drawImage(myImage, 10, 10, this); 
    } 

 
Wykonanie programu powoduje wyświetlenie obrazu znajdującego się w pliku Bubbles.gif
 
 
_________________________________________________________________________________________ 

Grafika  

 
 
Dołączona  do  języka  biblioteka  AWT  umożliwia  wykreślanie  podstawowych  obiektów  graficznych:  tekstów
odcinków,  prostokątów,  elips,  okręgów,  łuków  i  wielokątów.  Wykreślanie  odbywa  się  za  pomocą  metod  klasy 
abstrakcyjnej Graphics
 
 

public void drawString(String str, int x, int y) 

Wykreślenie tekstu str w prostokącie, którego lewy-dolny narożnik ma współrzędne (x,y). 
 

public void drawLine(xA, yA, xZ, yZ) 

Wykreślenie odcinka łączącego punkty o współrzędnych (xAyA) i (xZyZ). 
 

public void drawRect(int x, int y, int w, int h) 

background image

 

123 

Wykreślenie  prostokąta  o  współrzędnych  lewego-górnego  wierzchołka  (x,y)  i  rozmiarach  w x h  pikseli  (w  
szerokość, h - wysokość). 
 

public void drawOval(int x, int y, int w, int h) 

Wykreślenie owalu (okręgu albo elipsy) wpisanego w niewidoczny prostokąt, o współrzędnych lewego-górnego 
wierzchołka (x,y) i rozmiarach w x h pikseli. 
 

public void drawArc(int x, int y, int w, int h,  
                    int f, int t) 

Wykreślenie  łuku  wpisanego  w  niewidoczny  prostokąt,  o  współrzędnych  lewego-górnego  wierzchołka  (x,y)  i 
rozmiarach w x h pikseli, od kąta początkowego f do kąta końcowego t
 

public void drawPolygon(int x[], int y[], int n) 

Wykreślenie linii łamanej łączącej n punktów o współrzędnych (x[i]y[i]). 
 

public void fillRect(int x, int y, int w, int h) 

Wykreślenie wypełnionego prostokąta (por. drawRect). 
 

public void fillOval(int x, int y, int w, int h) 

Wykreślenie wypełnionego owalu (por. drawOval). 
 

public void fillPolygon(int x[], int y[], int n) 

Wykreślenie wypełnionego wielokąta (por. drawPolygon). 
 
 

Kontekst 

 
Z  każdym  dającym  się  wykreślić  komponentem  jest  związany  kontekst  graficzny.  Informacje  o  kontekście 
znajdują się w obiekcie wykreślacza. Odnośnik do wykreślacza jest dostępny poprzez parametr metody paint
Można go także otrzymać za pomocą metody  getGraphics
 
 

public Graphics getGraphics() 

Metoda  zwraca  odnośnik  do wykreślacza związanego  z  komponentem, albo odniesienie  null, jeśli komponent 
nie może pojawić się na ekranie. 
 
 
Uwaga:  Zaleca  się,  aby  po  umieszczeniu  w  wykreślaczu  pewnej  informacji  (np.  rodzaju  czcionki  albo  koloru 
pióra)  przywrócono jego stan pierwotny. Dzięki temu uniknie się kolizji wynikających z użycia tego samego 
wykreślacza przez różne komponenty. 
 
Na przykład 
 

public void draw(Graphics gDC) 

    Font oldFont = gDC.getFont(); 
    Color oldColor = gDC.getColor(); 
    // ... 
    Font font = new Font("TimesRoman", Font.BOLD, 80); 
    gDC.setFont(font); 
    gDC.setColor(Color.red); 
    gDC.drawString("Hello", 10, 20); 
    // ... 
    gDC.setColor(oldColor); 
    gDC.setFont(oldFont); 

 
Po  zakończeniu  wykonywania  metody  draw,  wykreślacz  identyfikowany  przez  odnośnik  gDC  znajduje  się  w 
takim samym stanie jak w chwili rozpocz
ęcia wykonywania tej metody. 
 
Następujący aplet, pokazany podczas wykonania na ekranie Konteksty graficzne, ilustruje zasady posługiwania 
się wykreślaczami. 

background image

 

124 

 
Ekran Konteksty graficzne 

### getgdc.gif 

 

<applet code=Master.class width=160 height=100> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public class Master extends Applet { 
    public void paint(Graphics gDC) 
    { 
        gDC.drawString("Press to draw a Circle", 20, 20); 
    } 
    public boolean mouseDown(Event evt, int x, int y) 
    { 
        Graphics gDC = getGraphics(); 
        gDC.drawOval(x-10, y-10, 20, 20);  // wykre

ś

lenie 

        return true; 
    } 
    public boolean mouseUp(Event evt, int x, int y) 
    { 
        Graphics gDC = getGraphics(); 
        update(gDC);                       // wyczyszczenie 
        return true; 
    } 

 
Wywołanie  metody  getGraphics  powoduje  dostarczenie  odnośnika  do  wykreślacza  związanego  z  pulpitem 
apletu. 
 
Naciśnięcie klawisza myszki powoduje wykreślenie okręgu o środku w punkcie, w którym znajduje się kursor, a 
zwolnienie przycisku powoduje usuni
ęcie okręgu z ekranu. 
 
 

Współrzędne 

 
Układ współrzędnych pojemnika (np. ramki apletu) ma punkt początkowy w lewym-górnym narożniku. Odcięte 
(x) zwiększają się prawo, a rzędne (y) do dołu. 
 
Podczas wykreślania obiektu podaje się lewą-górną współrzędną oraz rozmiary: poziomy i pionowy
 

public void paint(Graphics gDC) 

    gDC.drawRect(10, 20, 100, 200); // (x, y, w, h) 

 
Wyjątek od tej zasady dotyczy wykreślania napisów. W tym wypadku podaje się współrzędne lewego-dolnego 
narożnika niewidocznej linii na której spoczywa napis. 
 
Następujący  aplet,  pokazany  podczas  wykonania  na  ekranie  Współrzędne  komponentów,  ilustruje  zasadę 
określania współrzędnych napisów i grafiki. 
 
Ekran Współrzędne komponentów 

### over.gif 

 

<applet code=Master width=160 height=60> 
</applet> 
========================================= 

 

import java.applet.*; 
import java.awt.*; 
 
public 

background image

 

125 

class Master extends Applet { 
    public void paint(Graphics gDC) 
    { 
        gDC.drawString("Text over rectangle", 10, 20); 
        gDC.drawRect(10, 20, 80, 5); 
    } 

 
Mimo  iż  w  obu przypadkach podano te same współrzędne narożnika, niewidoczny prostokąt, w który wpisano 
napis nie pokrywa si
ę z prostokątem wykreślonym jawnie. 
 
 

Pulpit 

 
Ta część obszaru pojemnika, na której odbywa się wykreślanie grafiki (odcinkówowaliprostokątów, itp.) jest 
jego  pulpitem.  Podczas  wstawiania  komponentów  do  pojemnika  używa  się  współrzędnych  pojemnika,  ale 
podczas  wykreślania  grafiki  oraz  obsługiwania  zdarzeń  związanych  z  operacjami  wykonywanymi  za  pomocą 
myszki używa się współrzędnych pulpitu
 
 

Wcięcia 

 
Obszar  pulpitu  jest  wcięty  względem  obszaru  pojemnika.  Rozmiary  pojemnika  można  określić  za  pomocą 
metody size, a rozmiary wcięć za pomocą metody insets.  
 
Uwaga:  Domniemane  wcięcia  dla  apletu  mają  wartość  0.  A  zatem  obszar  i  pulpit  apletu  pokrywają  się.  W 
systemie Windows 95 domniemane wcięcia dla ramki mają wartości 42344
 
 

public Dimension size() 

Metoda zwraca odnośnik do obiektu, którego pola width height określają poziomy i pionowy rozmiar obszaru 
pojemnika. 
 
public Insets insets() 
Metoda  zwraca  odnośnik  do  obiektu,  którego  pola  left  (lewe),  top  (górne),  right  (prawe)  i  bottom  (dolne
określają wcięcia krawędzi pulpitu względem krawędzi obszaru pojemnika. 
 
 
Następujący  aplet,  pokazany  podczas  wykonania  na  ekranie  Pulpit  apletu  i  ramki,  ilustruje  zasady 
uwzględniania wcięć. 
 
Ekran Pulpit apletu i ramki 

### insets.gif 

 

<applet code=Master width=160 height=100> 
</applet> 
========================================= 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    Frame frame; 
    Button hello, world; 
    int w, h; 
    public void init() 
    { 
        setLayout(null); 
        add(hello = new Button("Hello")); 
        hello.reshape(1, 1, 60, 40); 
 
        frame = new MyFrame("Frame View"); 
        Insets ins = frame.insets(); 

background image

 

126 

        frame.add(world = new Button("World")); 
        int l = ins.left,               // 4 
            t = ins.top,                // 23 
            r = ins.left,               // 4 
            b = ins.bottom;             // 4 
        world.reshape(l+1, t+1, 60, 40); 
        Dimension size = size();        // 160 x 100 
        int w = size.width  + (l + r), 
            h = size.height + (t + b); 
        frame.resize(w, h); 
    } 
        public void paint(Graphics gDC) 
    { 
        Dimension dim = size(); 
        Insets ins = insets(); 
        int w = dim.width  - (ins.left + ins.right), 
            h = dim.height - (ins.top  + ins.bottom); 
        gDC.drawRect(0, 0, w-1, h-1); 
    } 
    public boolean mouseUp(Event evt, int x, int y) 
    { 
        Dimension dim = size(); 
        Insets ins = insets(); 
        int w = dim.width, 
            h = dim.height; 
        Graphics gDC = getGraphics(); 
        int xC = w/2, 
            yC = h/2; 
        gDC.drawLine(x, y, xC, yC); 
        return true; 
    } 

 
class MyFrame extends Frame { 
    MyFrame(String caption) 
    { 
        super(caption); 
        show(); 
    } 
    public void paint(Graphics gDC) 
    { 
        Dimension dim = size(); 
        Insets ins = insets(); 
        int w = dim.width  - (ins.left + ins.right), 
            h = dim.height - (ins.top  + ins.bottom); 
        gDC.drawRect(0, 0, w-1, h-1); 
    } 
    public boolean mouseUp(Event evt, int x, int y) 
    { 
        Dimension dim = size(); 
        Insets ins = insets(); 
        int w = dim.width  - (ins.left + ins.right), 
            h = dim.height - (ins.top  + ins.bottom); 
        Graphics gDC = getGraphics(); 
        int xC = w/2, 
            yC = h/2; 
        gDC.drawLine(x, y, xC, yC); 
        return true; 
    } 

 
Program napisano w taki sposób, aby rozmiar pulpitu ramki był identyczny z rozmiarem pulpitu apletu. W celu 
uwidocznienia pulpitów wykre
ślono na nich prostokąty o maksymalnych rozmiarach. 
 
Jak wynika z rozpatrzenia kodu programu, umieszczenie przycisku na pulpicie ramki oraz wykreślenie odcinka 
ł
ączącego  punkt  kliknięcia  ze  środkiem  ramki  wymaga  uwzględnienia  wcięć.  Problemy  te  nie  występują  dla 
apletów. 
 
 

Obcinanie 

background image

 

127 

 
Jeśli  wykres  nie  mieści  się  na  pulpicie  apletu,  to  jest  obcinany.  Dzięki  temu  nic  nie  stoi  na  przeszkodzie 
"wykreślania" obiektów, które nie mieszczą się na pulpicie. 
 
Domyślnym  obszarem  obcinania  jest  pulpit  apletu.  Za  pomocą  metody  clipRect  wywołanej  na  rzecz 
wykreślacza, można jako obszar obcinania określić dowolny inny prostokąt. 
 
 

public void clipRect(int x, int, y, int width, int height) 

Wywołanie metody powoduje określenie jako obszaru obcinania, prostokąta o współrzędnych narożnika (x,y) i 
rozmiarach h.. 
 
W  następującym  programie,  pokazanym  podczas  wykonywania  na  ekranie  Obszar  obcinania,  wykreślono 
prostokąt,  a  następnie  zajęty  przezeń  obszar  zdefiniowano  jako  obszar  obcinania.  Dlatego  wykres  jednego  z 
okręgów jest obcięty do wybranego prostokąta. 
 
Ekran Obszar obcinania 

### clip.gif 

 
Przykład Obcinanie 
 

<applet code=Master width=160 height=80> 
</applet> 
========================================= 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    public void paint(Graphics gDC) 
    { 
        gDC.drawRect(25, 25, 50, 50); 
        gDC.clipRect(25, 25, 50, 50); 
        gDC.drawOval(0, 25, 50, 50); 
        Dimension size = this.size(); 
        int w = size.width, h = size.height; 
        gDC.clipRect(0, 0, w, h); 
        gDC.drawOval(50, 25, 50, 50); 
    } 

 
Przez ustawienie jako obszaru obcinania całego pulpitu apletu, przywrócono obcinanie domyślne. 
 
 

Czcionki 

 
Czcionkę  charakteryzuje  rozmiar,  krój  i  styl.  Na  przykład  tytuł  niniejszego  podrozdziału  jest  napisany  14-
punktow
ą czcionką TimesRoman, w stylu Bold
 
Mając  na  względzie  przenośność  wyglądu  czcionki  zaleca  się  posługiwać  tylko  czcionkami  o  nazwach 
podanych w tabeli Czcionki (dla porównania podano ich odpowiedniki w systemie Windows). 
 
Uwaga: Czcionka domyślna ma rozmiar 12 punktów i styl zwykły
 
Tabela Czcionki 

### 
 

Java 

Windows 

 

 
 

TimesRoman 

Times New Roman 

 

Helvetica 

Arial 

 

Courier 

Courier New 

 

Dialog 

MS Sans Serif 

 

Symbols 

WingDing 

background image

 

128 

### 

 
Do zapisania stylu można posłużyć się symbolami podanymi w tabeli Style czcionki
 
Tabela Style czcionki 

### 
 

Styl 

Symbol 

 
 

zwykły 

Font.PLAIN 

 

kursywa 

Font.ITALIC 

 

pogrubiony 

Font.BOLD 

 

pogrubiona kursywa 

Font.BOLD + Font.ITALIC 

### 

 
 
Czcionka  jest  reprezentowana  przez  obiekt  klasy  Font.  W  wywołaniu  jej  konstruktora  określa  się  krój,  styl  
rozmiar czcionki. 
 
 

public Font(String name, int style, int size) 

Konstruktor tworzy obiekt czcionki o podanym krojustylu rozmiarze. Jeśli w danym Systemie taka czcionka 
nie istnieje, to zostanie utworzona czcionka maksymalnie zbliżona do wymaganej. 
 
 
Następujący program, pokazany podczas wykonania na ekranie Kroje i style, wyświetla zestaw 12 pt czcionek 
o różnych krojach i stylach.  
 
Ekran Kroje i style 

### typeface.gif 

 

<applet code=Master.class width=160 height=200> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    String string; 
    String typeFace[] =  {  
                             "TimesRoman",  
                             "Helvetica",  
                             "Courier"  
                         }; 
    int style[] =        {  
                             Font.PLAIN,  
                             Font.BOLD,  
                             Font.ITALIC,  
                             Font.BOLD + Font.ITALIC 
                         }; 
    String styleName[] = {  
                             " plain", 
                             " bold",  
                             " italic",  
                             " bold-italic"  
                         }; 
    public void paint(Graphics gDC) 
    { 
        for(int n = 1, i = 0; i < typeFace.length ; i++) { 
            String fontName = typeFace[i]; 
            for(int j = 0; j < style.length ; j++) { 
                int fontStyle = style[i]; 
                Font font = new Font(fontName, fontStyle, 12); 
                gDC.setFont(font); 
                string = fontName + styleName[j]; 
                gDC.drawString(string, 10, 15 * n++); 
            } 

background image

 

129 

        } 
    } 

 
 

Metryka 

 
Kompletny  zestaw  właściwości  czcionki  opisuje  jej  metryka.  Metody  operujące  na  metryce  należą  do  klasy 
abstrakcyjnej FontMetrics
 
Uwaga:  Badanie  metryki  może  dotyczyć  tylko  czcionki  takiego  komponentu,  który  może  być  wykreślony  na 
ekranie. 
 
Ważnymi parametrami metryki są 
 
 

uniesienie 

(ascent

odległość między liną bazową, a szczytem znaku, 
jak w literze Ý, 

 

obniżenie 

(descent

odległość między linią bazową, a podstawą znaku, 
jak w literze g, 

 

ś

wiatło 

(leading

odstęp poniżej podstawy znaku, 

 

wysokość 

(height

suma uniesienia, obniżenia i światła. 

 
Uwaga: Linią bazową (base line) jest linia na której spoczywają znaki, np. jak w Hello
 
 

public int getAscent() 

Metoda zwraca uniesienie w pikselach. 
 

public int getDescent() 

Metoda zwraca obniżenie w pikselach. 
 

public int getLeading() 

Metoda zwraca światło w pikselach. 
 

public int getHeight() 

Metoda zwraca wysokość czcionki w pikselach. 
 
 
Następujący  program,  pokazany  podczas  wykonania  na  ekranie  Metryka  czcionki,  ilustruje  posługiwanie  się 
ważniejszymi parametrami czcionki. 
 
Ekran Metryka czcionki 

### metrics.gif 

 

<applet code=Master.class width=300 height=150> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    Font font = new Font("Arial", Font.BOLD, 80); 
    static String string = "ÝfgA"; 
    static int xBase = 10, yBase; 
    int strAscent, strDescent, strHeight, strLeading; 
    static int strWidth; 
    public void init() 
    { 
        Toolkit toolKit = Toolkit.getDefaultToolkit(); 
        FontMetrics mtx = toolKit.getFontMetrics(font); 
        strWidth = mtx.stringWidth(string); 
        strAscent  = mtx.getAscent(); 

background image

 

130 

        strDescent = mtx.getDescent(); 
        strHeight  = mtx.getHeight(); 
        strLeading = strHeight - (strAscent + strDescent); 
        yBase = strHeight + 10; 
    } 
    public void paint(Graphics gDC) 
    { 
        gDC.setFont(font); 
        gDC.drawString(string, xBase + 50, yBase); 
        draw(gDC, "BaseLine", 0); 
        draw(gDC, "AscentLine", strAscent); 
        draw(gDC, "DescentLine", -strDescent); 
        draw(gDC, "", -strDescent-strLeading); 
    } 
    static void draw(Graphics gDC, String str, int h) 
    { 
        int xBase = Master.xBase, 
            yBase = Master.yBase; 
        gDC.drawLine(xBase, yBase-h, 
                     xBase+ 50 + 10 + strWidth, yBase-h); 
        Font oldFont = gDC.getFont(), 
             newFont = new Font("TimesRoman", Font.ITALIC, 10); 
        gDC.setFont(newFont); 
        gDC.drawString(str, xBase, yBase-h); 
        gDC.setFont(oldFont); 
    } 

 
 

Kolory  

 
Wzorcowym  modelem  koloru  jest  RGB.  W  modelu  RGB  każdy  kolor  można  przedstawić  jako  trójkę 
składników 
 
 

RGB 

 
w  której  R  (red),  G  (green),  B  (blue)  określają  ile  w  kolorze  jest  składnika  czerwonego,  zielonego  i 
niebieskiego
 
W szczególności, kolor RGB(255,255,0), w którym występuje maksymalna ilość czerwieni i zieleni, ale nie ma 
składnika niebieskiego, jest kolorem żółtym
 
W programie wynikowym kolor jest reprezentowany przez czwórkę bajtów 
 
 

aRGB 

 
w  której a jest składnikiem  alfa, reprezentującym  przeźroczystość koloru, a  RGB są składnikami koloru o 
wartościach z przedziału 0..255 włącznie.  
 
Jeśli składnik alfa ma wartość 0, to kolor jest całkowicie przeźroczysty (transparent), a jeśli ma wartość 255, to 
jest całkowicie nieprzeźroczysty (opaque). Domniemaną wartością alfa jest 255
 
Kilkanaście typowych kolorów ma oznaczenia symboliczne podane w tabeli Symbole kolorów
 
Tabela Symbole kolorów 

### 
 

Color.black 

Color.blue 

Color.cyan 

 

Color.darkGray 

Color.gray 

Color.green 

 

Color.lightGray 

Color.magenta 

Color.orange 

 

Color.pink 

Color.red 

Color.white 

 

 

Color.yellow 

### 

 
 

background image

 

131 

Kolor bieżący 

 
Z każdym kontekstem graficznym jest związany kolor bieżący. Ustawienie i pobranie koloru bieżącego odbywa 
się za pomocą procedur setColor i getColor
 
 

public void setColor(Color color) 

Metoda ustawia kolor bieżący na kolor podany. 
 

public Color getColor() 

Metoda zwraca odnośnik do odrębnego obiektu opisującego kolor bieżący.  
 
 
Następujący  program,  pokazany  podczas  wykonywania  na  ekranie  Model  kolorów,  ilustruje  sposób 
przekazywania informacji o kolorze z opisu apletu 
 
Ekran Model kolorów 

### rgbcolor.gif 

 

<applet code=Master.class width=160 height=60> 
<param name=Color value=ff00ff> 
</applet> 
============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    Color color; 
    int rgbColor = 0;; 
    public void init() 
    { 
        String color = getParameter("Color"); 
        try { 
            rgbColor = Integer.parseInt(color, 16); 
        } 
        catch(NumberFormatException e) { 
            showStatus("Wrong format"); 
        } 
        this.color = new Color(rgbColor); 
    } 
    public void paint(Graphics gDC) 
    { 
        gDC.setColor(color); 
        gDC.drawString("Hello", 20, 20); 
    } 

 
Wykonanie programu powoduje wyświetlenie napisu Hello w kolorze purpurowym
 
Gdyby  parametrowi  Color  nadano  wartość  0xff00ff,  to  spowodowałoby  to  wysłanie  wyjątku  klasy 
NumberFormatException, a napis zostałby wy
świetlony w kolorze czarnym
 
 

Kolor lica i kolor tła 

 
Z  każdym  komponentem  są  związane  dwa  kolory:  kolor  lica  (foreground)  i  kolor  tła  (background).  Do 
zarządzania nimi służą metody getForeground i setForeground oraz getBackground i setBackground
 
 

public void setForeground(Color color) 

Metoda ustawia kolor lica komponentu na podany. 
 

public void setBackground(Color color) 

background image

 

132 

Metod ustawia kolor tła komponentu na podany. 
 

public Color getForeground() 

Metoda zwraca kolor lica komponentu. 
 

public Color getBackground() 

Metoda zwraca kolor tła komponentu. 
 
 
Następujący aplet, pokazany podczas wykonania na ekranie Kolory lica i tła, ilustruje zasadę określania koloru 
komponentów. 
 
Ekran Kolory lica i tła 

### foreback.gif 

 

<applet code=Master width=160 height=100> 
</applet> 
========================================= 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    Button button; 
    public void init() 
    { 
        add(button = new Button("OK")); 
        setBackground(Color.white); 
        setForeground(Color.red); 
    } 
    public void paint(Graphics gDC) 
    { 
        gDC.drawOval(10, 10, 80, 80); 
    } 
    public boolean action(Event evt, Object arg) 
    { 
        boolean flag = evt.target == button; 
        if(flag) 
            button.setBackground(Color.black); 
        return flag; 
    } 
}    

 
Kliknięcie przycisku powoduje zmianę jego koloru na czarny. 
 
 

Tryb XOR 

 
Często  istnieje  potrzeba  wykreślenia  i  wytarcia  wykreślonego  obiektu,  ale  bez  naruszenia  tła.  Do  tego  celu 
dobrze  nadaje  się  wykreślanie  w  trybie  XOR.  Ma  ono  tę  właściwość,  że  dwukrotne  wykreślenie  tego  samego 
obiektu przywraca pierwotny stan tła. 
 
 

public void setXORMode(Color color) 

Metoda  przygotowuje  wykreślacz  do  wykreślania  w  trybie  XOR  z  kolorem  color.  Powoduje  to,  że  piksel 
wykreślony  w  kolorze  bieżącym  zachowa  ten  kolor  na  tle  o  podanym  kolorze,  a  na  tle  w  swoim  kolorze 
przyjmie  podany  kolor.    Zmiana  pozostałych  kolorów  nie  jest  przewidywalna,  ale  po  ponownym  wykreśleniu 
nastąpi przywrócenie kolorów pierwotnych. 
 
 
Następujący  aplet,  pokazany  podczas  wykonania  na  ekranie  Odtwarzanie  tła,  umożliwia  przeciąganie  okręgu 
bez naruszenia kolorowego napisu stanowiącego tło. 
 

background image

 

133 

Ekran Odtwarzanie tła 

### xormode.gif 

 
Przykład Odtwarzanie tła 
 

<applet code=Master width=400 height=100> 
</applet> 
========================================= 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    Font font = new Font("TimesRoman", Font.BOLD, 60); 
    final int r = 20; 
    int xOld, yOld; 
    public void init() 
    { 
        setBackground(Color.green); 
    } 
    public void paint(Graphics gDC) 
    { 
        gDC.setFont(font); 
        gDC.setColor(Color.red); 
        gDC.drawString("Drag over me!", 10, 60); 
    } 
    public boolean mouseDown(Event evt, int x, int y) 
    { 
        drawRing(xOld = x, yOld = y); 
        return true; 
    } 
    public boolean mouseDrag(Event evt, int x, int y) 
    { 
        drawRing(xOld, yOld); 
        drawRing(xOld = x, yOld = y); 
        return true; 
    } 
    public boolean mouseUp(Event evt, int x, int y) 
    { 
        drawRing(x, y); 
        return true; 
    } 
    void drawRing(int x, int y) 
    { 
        Graphics gDC = getGraphics(); 
        gDC.setColor(Color.red); 
        gDC.setXORMode(Color.green); 
        int l = x-r, t = y-r, w = 2*r, h = 2*r; 
        for(int i = 0; i < 5 ; i++) 
            gDC.drawOval(l+i, t+i, w-2*i, h-2*i); 
     } 

 
W celu uproszczenia kodu apletu nie obsłużono sytuacji kiedy przeciąganie zakończy się poza pulpitem apletu. 
W takim przypadku przeci
ągany pierścień nie zostanie usunięty z ekranu. 
 
 
_________________________________________________________________________________________ 

Myszka 

 
W typowych przypadkach nie jest istotne, który z przycisków myszki został użyty do wykonania akcji. Jednak 
zbadanie stanu modyfikatorów Alt (przycisk środkowy) i Meta  (przycisk prawy) umożliwia dokonanie takiego 
rozstrzygnięcia. 
 

background image

 

134 

Jeśli  myszka  jest  wyposażona  w  przycisk  związany  z  modyfikatorem  Alt  albo  Meta,  to  jest  on  ustawiany 
niejawnie. W  przeciwnym razie ustawienie modyfikatora musi być zasymulowane przez naciśnięcie klawisza 
Alt albo Meta
 
W  szczególności,  jeśli  myszka  jest  dwu-przyciskowa,  to  użycie  prawego  przycisku  powoduje  niejawne 
ustawienie  modyfikatora  Meta,  ale  w  celu  zasymulowania  środkowego  przycisku  (i  ustawienia  modyfikatora 
Alt) należy wraz z użyciem przycisku myszki, dodatkowo nacisnąć klawisz Alt
 
Uwaga: Opisany efekt można zaobserwować wykonując aplet pod przeglądarką Netscape. W środowisku Cafe 
naciśnięcie klawisza Alt powoduje wyróżnienie menu Applet, a więc nie może być rozpoznane przez badanie 
modyfikatora. 
 
W  tabeli  Maski  klawiszy  wymieniono  maski  związane  z  modyfikatorami  Alt  i  Meta  oraz  dodatkowo  maski 
klawiszy Shift Ctrl
 
Tabela Maski klawiszy 

### 
 

Event.SHIFT_MASK 

naci

ś

ni

ę

to klawisz Shift 

 

Event.CTRL_MASK 

naci

ś

ni

ę

to klawisz Ctrl 

 

Event.ALT_MASK 

naci

ś

ni

ę

to klawisz Alt 

 

Event.META_MASK 

naci

ś

ni

ę

to klawisz Meta 

### 

 
Następujący  aplet,  pokazany  podczas  wykonania  na  ekranie  Przyciski  myszki,  wyświetla  nazwę  naciśniętego 
klawisza myszki. 
 
Ekran Przyciski myszki 

### mouse.gif 

 

<applet code=Master.class width=160 height=100> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    public void paint(Graphics gDC) 
    { 
        gDC.drawString("Press mouse button!", 10, 20); 
    } 
    public boolean mouseDown(Event evt, int x, int y) 
    { 
        int mods = evt.modifiers; 
        String button; 
        if((mods & Event.ALT_MASK) != 0) 
            button = "MIDDLE"; 
        else if((mods & Event.META_MASK) != 0) 
            button = "RIGHT"; 
        else 
            button = "LEFT"; 
        Graphics gDC = getGraphics(); 
        gDC.drawString("You pressed " + button +  
                       " button!", x, y); 
        return true; 
    } 
    public boolean mouseUp(Event evt, int x, int y) 
    { 
        repaint(); 
        return true; 
    } 

 
Jeśli zostanie naciśnięty prawy klawisz myszki, to w punkcie kliknięcia wyświetli się napis 
 

 

You pressed RIGHT button! 

background image

 

135 

 
 
_________________________________________________________________________________________ 

Klawiatura 

 
Zdarzenia  KEY_PRESS  i  KEY_RELEASE  dotyczą  zwykłych  klawiszy  (wszystkie  znaki  drukowalne,  a 
ponadto: EnterTabEscBackspaceDel), a zdarzenia KEY_ACTION i KEY_ACTION_RELEASE dotyczą 
klawiszy funkcyjnych
 
Klawisze funkcyjne mają oznaczenia podane w tabeli Klawisze funkcyjne
 
Tabela Klawisze funkcyjne 

### 
 

Event.HOME 

klawisz Home 

 

Event.END 

klawisz End 

 

Event.PGUP 

klawisz PgUp 

 

Event.PGDOWN 

klawisz PgDn 

 

Event.UP 

klawisz Up (strzalka w gór

ę

 

Event.DOWN 

klawisz Dn (strzałka w dół) 

 

Event.LEFT 

klawisz Lt (strzałka w lewo) 

 

Event.RIGHT 

klawisz Rt (strzałka w prawo) 

 
 

Event.F1 

klawisz F1 

 

Event.F2 

klawisz F2 

 

... 

... 

 

Event.F12 

klawisz F12 

### 

 
Badanie, czy wraz z  naciśnięciem klawisza naciśnięto  jeden z klawiszy modyfikujących: ShiftCtrlAltMeta 
odbywa się za pomocą masek. 
 
Następujący  aplet,  pokazany  podczas  wykonania  na  ekranie  Nazwy  klawiszy,  wyświetla  nazwę  naciśniętego 
klawisza. 
 
Ekran Nazwy klawiszy 

### keys.gif 

 

<applet code=Master.class width=360 height=80> 
</applet> 
============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    Font font = new Font("Arial", Font.BOLD, 60); 
    String keyName = ""; 
    public void paint(Graphics gDC) 
    { 
        gDC.setFont(font); 
        gDC.drawString(keyName, 20, 60); 
    } 
    public boolean keyDown(Event evt, int key) 
    { 
        if(evt.id == Event.KEY_PRESS) 
            keyName = getModKeys(evt) + getRegKeyName(evt); 
        else if(evt.id == Event.KEY_ACTION) 
            keyName = getModKeys(evt) + getFunKeyName(evt); 
        repaint(); 
        return true; 
    } 
    String getModKeys(Event evt) 
    { 
        int flags = evt.modifiers; 
        boolean notEnter = evt.key != 10; 
        String string = ""; 

background image

 

136 

        if(evt.shiftDown() && notEnter) 
            string += "Shift-"; 
        if((flags & Event.ALT_MASK) != 0 && notEnter) 
            string += "Alt-"; 
        if(evt.metaDown() && notEnter) 
            string += "Meta-"; 
        if(evt.controlDown() && notEnter) 
            string += "Ctrl-"; 
        return string; 
    } 
    String getRegKeyName(Event evt) 
    { 
        if(evt.key == 10) 
            return "Enter"; 
        char chr = (char)evt.key; 
        if(evt.controlDown() && chr < ' ') 
            return "" + (char)(chr + '`'); 
        else switch(chr) { 
            case '\t': 
                return "Tab"; 
            case '\33': 
                return "Esc"; 
            case '\10': 
                return "Backspace"; 
        } 
        if(chr == '\177') 
            return "Delete"; 
        if(chr == ' ') 
            return "Space"; 
        return "" + chr; 
    } 
    String getFunKeyName(Event evt) 
    { 
        int key = evt.key; 
        switch(key) { 
            case Event.HOME: 
                return "Home"; 
            case Event.END: 
                return "End"; 
            case Event.PGUP: 
                return "PgUp"; 
             case Event.PGDN: 
                return "PgDn"; 
            case Event.UP: 
                return "Up"; 
            case Event.DOWN: 
                return "Down"; 
            case Event.LEFT: 
                return "Lt"; 
            case Event.RIGHT: 
                return "Rt"; 
            default: 
                int offF1 = key - Event.F1; 
                return "F" + (1 + offF1); 
        } 
    } 

 
 
_________________________________________________________________________________________ 

Kursory 

 
Podczas  wykonywania  operacji  graficznych  w  oknach  można  posługiwać  się  zestawem  standardowych 
kursorów.  
 
Kształt  kursora  identyfikują  liczby  całkowite,  które  można  wyrażać  za  pomocą  symboli  podanych  w  tabeli 
Kursory.  Wygląd  kursora  zależy  od  użytego  systemu.  W  Windows  95  kursor  oczekiwania  (delay)  ma  wygląd 
klepsydry, a w MacOS ma wygląd zegarka
 

background image

 

137 

Tabela Kursory 

### 

 

Kursor 

Wyglą

 

 

Frame.DEAFAULT_CURSOR 

wska

ź

nik 

 

Frame.TEXT_CURSOR 

karetka 

 

Frame.WAIT_CURSOR 

oczekiwanie 

 

Frame.CROSSHAIR_CURSOR 

krzy

ż

ak 

 

Frame.HAND_CURSOR 

dło

ń

 

### 

 
 

public int getCursorType() 

Metoda zwraca liczbę identyfikującą kursor (np. Frame.TEXT_CURSOR). 
 

public void setCursor(int shape) 

Metoda określa kształt kursora jaki ma obowiązywać w oknie. 
 
 
Następujący aplet, pokazany podczas wykonania na ekranie Kształt kursora, umożliwia wykreślanie prostokąta 
za  pomocą  kursora  w  kształcie  krzyżaka  (za  sprawą  programu  PaintShopPro,  użytego  do  zdejmowania 
ekranów, krzyżak nie jest widoczny na przytoczonej ilustracji). 
 
Ekran Kształt kursora 

### cursor.gif 

 

<applet code=Master.class width=160 height=60> 
</applet> 
============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    Frame frame; 
    public void init() 
    { 
        frame = new MyFrame("Draw a box"); 
    } 
    public void paint(Graphics gDC) 
    { 
        gDC.drawString("Drag in FRAME", 10, 25); 
    } 

 
class MyFrame extends Frame { 
    Graphics gDC; 
    MyFrame(String caption) 
    { 
        super(caption); 
        resize(150, 120); 
        show(); 
        gDC = getGraphics(); 
    } 
    int xAnchor, yAnchor, 
        xOld, yOld; 
    Color backColor; 
    public boolean mouseDown(Event evt, int x, int y) 
    { 
        xAnchor = xOld = x; 
        yAnchor = yOld = y; 
        backColor = getBackground(); 
        return true; 
    } 
    int abs(int par) 
    { 
        if(par < 0) 

background image

 

138 

            return -par; 
        return par; 
    } 
    int min(int one, int two) 
    { 
        if(one < two) 
            return one; 
        return two; 
    } 
    public boolean mouseDrag(Event evt, int x, int y) 
    { 
        Color color = gDC.getColor(); 
        gDC.setColor(backColor); 
        int w = xOld - xAnchor, 
            h = yOld - yAnchor; 
        int l = min(xAnchor, xOld), 
            t = min(yAnchor, yOld); 
        gDC.drawRect(l, t, abs(w), abs(h)); 
        gDC.setColor(color); 
        w = (xOld = x) - xAnchor; 
        h = (yOld = y) - yAnchor; 
        l = min(xAnchor, x); 
        t = min(yAnchor, y); 
        gDC.drawRect(l, t, abs(w), abs(h)); 
        return true; 
    } 
    public boolean mouseUp(Event evt, int x, int y) 
    { 
        repaint(); 
        return true; 
    } 

 
Wykreślany prostokąt znika w chwili zwolnienia przycisku myszki. 
 
_________________________________________________________________________________________ 

Obrazy 

 
Obrazy  są  ładowane  z  plików.  W  celu  załadowania  obrazu,  znajdującego  się  w  pliku  sieci  globalnej,  należy 
podać jego lokalizator URL.  
 
Ponieważ  często  zdarza  się,  że  ładowany  obraz  znajduje  się  w  tym  samym  katalogu  co  kod  albo  opis  apletu, 
dobrze jest pamiętać o metodach getCodeBase i getDocumentBase
 
Rozpoznawane  są  tylko  obrazy  zapisane  w  formatach  GIF  i  JPEG.  Inne  formaty  wymagają  własnego 
dekodowania. 
 
Format  GIF  stosuje  kompresję  bezstratną,  a  format  JPEG  stosuje  transformację  z  utratą  elementów  obrazu. 
Dlatego  format  GIF  jest  przydatny  do  przekazywania  tekstów  i  zrzutów  ekranu,  a  format  JPEG  do 
przekazywania fotografii
 
Ładowanie obrazów odbywa się za pomocą metod getImage, a ich wykreślanie za pomocą metod drawImage
Jeśli aplet podejmie próbę załadowania obrazu z innego miejsca niż sam pochodzi, to zostanie wysłany wyjątek 
klasy SecurityException
 
 

public Image getImage(URL whereURL) 

Zapoczątkowanie pobrania obrazu z miejsca określonego przez lokalizator whereUrl
 

public Image getImage(URL baseURL, String fileName) 

Zapoczątkowanie pobrania obrazu z pliku fileName znajdującego się w katalogu określonym przez baseURL
 

public boolean drawImage(Image img, int x, int y,  
                         ImageObserver imgObs) 

background image

 

139 

Zapoczątkowanie  wykreślania  obrazu  img,  pod  nadzorem  obserwatora  imgObs,  w  prostokącie,  którego  lewy-
górny narożnik znajduje się w punkcie (xy).  
 

public boolean drawImage(Image img, int x, int y,  
                         int width, int height, 
                         ImageObserver imgObs) 

Zapoczątkowanie  wykreślania  przeskalowanego  obrazu  img,  pod  nadzorem  obserwatora  imgObs,  w 
prostokącie o rozmiarach width x height, którego lewy-górny narożnik znajduje się w punkcie (xy).  
 

public boolean drawImage(Image img, int x, int y,  
                         int width, int height,  
                         Color backColor, 
                         ImageObserver imgObs) 

Zapoczątkowanie  wykreślania  przeskalowanego  obrazu  img,  pod  nadzorem  obserwatora  imgObs,  w 
prostokącie o rozmiarach width x height, którego lewy-górny narożnik znajduje się w punkcie (xy) i z kolorem 
tła backColor (istotne podczas wykreślania obrazów z przeźroczystymi pikselami). 
 
 
Wykonanie  następującego  programu,  pokazanego  podczas  wykonania  na  ekranie  Obraz  WinZip,  powoduje 
wyświetlenie  obrazu  z  pliku  WinZip.gif,  znajdującego  się  w  tym  samym  katalogu  co  plik  zawierający  opis 
apletu. 
 
Ekran Obraz WinZip 

### grab.gif 

 

<applet code=Master.class width=420 height=100> 
<param name=Grab value=WinZip.gif> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
import java.net.*; 
 
public 
class Master extends Applet { 
    private static Image myImage; 
    int width, height; 
    public void init() 
    { 
        String what = getParameter("Grab"); 
        URL where   = getDocumentBase(); 
        myImage = getImage(where, what); 
        width   = myImage.getWidth(this); 
        height  = myImage.getHeight(this); 
    } 
    public void paint(Graphics gDC) 
    { 
        gDC.drawImage(myImage, 0, 0, width, height,  
                      Color.black, this); 
    } 

 
Informacje o rozmiarach apletu pobrano z jego opisu. 
 
Obraz przeskalowano w taki sposób, aby zajął cały pulpit apletu. 
 
 

Obserwacja  

 
Mimo  mylącej  nazwy,  wykonanie  metody  getImage  jedynie  zapoczątkowuje  ściągnięcie  obrazu  z  sieci. 
Dopiero wykonanie metody drawImage powoduje sukcesywne, w miarę postępującej transmisji, wyświetlanie 
kolejnych fragmentów obrazu. 
 

background image

 

140 

To  sukcesywne  wyświetlanie  jest  nadzorowane  przez  obserwatora,  którym  jest  obiekt  klasy  implementującej 
interfejs ImageObserver
 
Interfejs ImageObserver implementuje m.in. klasa Component, a więc i klasa Applet. Dlatego w wywołaniu 
metod drawImage może wystąpić odnośnik this identyfikujący obserwatora. 
 
Uwaga: Interfejs ImageObserver zawiera deklarację tylko jednej metody: imageUpdate. 
 
 

public boolean imageUpdate(Image img, int flags,  
                           int x, int y,  
                           int width, int height) 

Metoda imageUpdate jest wywoływana przez System wielokrotnie, w miarę postępującego ładowania obrazu. 
Jeśli zwróci wartość true, to będzie wywołana ponownie, a jeśli zwróci false, to dla danej operacji nie będzie 
już wywoływana. 
 
 
W ciele metody imageUpdate można odwoływać się m.in. do następujących flag bitowych 
 

WIDTH 

Jest dostępna szerokość obrazu, określona przez parametr width
 

HEIGHT 

Jest dostępna wysokość obrazu, określona przez parametr height
 

SOMEBITS 

Jest dostępna następna porcja bitów obrazu. Ograniczający ją prostokąt jest określony łącznie przez parametry 
(x,y) i width x height
 

ALLBITS 

Jest dostępny cały obraz. 
 

ERROR 

Wystąpił błąd. Inne informacje nie będą już dostarczone. Obrazu nie da się wykreślić. 
 
 
Następujący  aplet,  pokazany  podczas  wykonania  na  ekranie  Obserwowanie  ładowania,  ilustruje  zasadę 
posługiwania się obserwatorem ładowania obrazu. 
 
Ekran Obserwowanie ładowania 

### observer.gif 

 

<applet code=Master.class width=160 height=100> 
<param name=Photo value=Duke.gif> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
import java.awt.image.*;  
import java.net.*;   
 
public 
class Master extends Applet implements ImageObserver { 
    boolean done; 
    private static Image myImage; 
    public void init() 
    { 
        String what = getParameter("Photo"); 
        URL where = getDocumentBase(); 
        myImage = getImage(where, what); 
    } 
    public void paint(Graphics gDC) 
    { 

background image

 

141 

        done = gDC.drawImage(myImage, 0, 0, this); 
    } 
    public boolean imageUpdate(Image img, int flags,  
                               int x, int y,  
                               int width, int height) 
    { 
        if((flags & ImageObserver.ALLBITS) != 0) { 
            showStatus("Image fully loaded"); 
            Graphics gDC = getGraphics(); 
            gDC.drawImage(img, x, y, null);  // wykre

ś

lenie 

            return false; 
         } else { 
            if((flags & ImageObserver.HEIGHT) == 0) 
                showStatus("Loading ... "); 
            else 
                showStatus("Loaded " + height); 
            return true; 
        } 
    } 

 
Przebieg wykreślania obrazu jest obserwowany. Komunikaty o przebiegu ładowania są wyświetlane w wierszu 
stanu apletu. 
 
 

Nadzór 

 
Nadzorowanie  ładowania  większej  liczby  obrazów  zapewnia  nadzorca  mediów.  Jego  rolę  pełni  obiekt  klasy 
MediaTracker
 
Dzięki nadzorcy mediów można zainicjować ładowanie większej liczby obrazów, na przykład zestawu kadrów 
animacji, a do czasu ich ściągnięcia z sieci wykonywać inne czynności. 
 
Każda  operacja  zlecona  nadzorcy  jest  rejestrowana  i  opatrywana  unikatowym  identyfikatorem.  Zapytania  o 
przebieg wykonania operacji wymagają podania identyfikatora. 
 
Kilka  metod  nadzorcy  mediów  zwraca  flagi  bitowe  określające  postęp  ładowana  obrazów.  Do  ich  badania 
można użyć symboli wymienionych w tabeli Flagi ładowania
 
Tabela Flagi ładowania 

### 

 

LOADING 

Trwa ładowania 

 

ABORTED 

Ładowanie/ładowania zarzucono 

 

ERRORED 

Ładowanie/ładowania nie powiodły się 

 

COMPLETE 

Ładowanie/ładowania zakończono 

### 

 
 

boolean waitForID(int id) 

Metoda  wstrzymuje  wykonanie  wątku  do  chwili  zakończenia  ładowania  wszystkich  obrazów  opatrzonych 
podanym identyfikatorem. 
 

boolean waitForAll() 

Metoda wstrzymuje wykonanie wątku do chwili zakończenia ładowania wszystkich obrazów. 
 

int statusId(int id, boolean load) 

Metoda zwraca flagi opisujące przebieg ładowania obrazu o podanym identyfikatorze. 
 

int statusAll(boolean load) 

Metoda zwraca flagi opisujące ładowanie wszystkich obrazów. 
 
 

background image

 

142 

Następujący  aplet,  pokazany  podczas  wykonania  na  ekranie  Nadzorowanie  ładowania,  ilustruje  użycie 
nadzorcy mediów. 
 
Ekran Nadzorowanie ładowania 

### tracker.gif 

 

<applet code=Master.class width=220 height=100> 
<param name=Count  value=3> 
<param name=Prefix value=Photo> 
<param name=Photo1 value=Duke1.gif> 
<param name=Photo2 value=Duke2.gif> 
<param name=Photo3 value=Duke3.gif> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
import java.net.*; 
import java.util.*; 
 
public 
class Master extends Applet { 
    int count; 
    Image photos[]; 
    static MediaTracker tracker; 
    int width, height; 
    public void init() 
    { 
        tracker = new MediaTracker(this); 
        URL where = getCodeBase(); 
        try { 
            String count = getParameter("Count"); 
            this.count = Integer.parseInt(count); 
        } 
        catch(NumberFormatException e) 
        { 
            this.count = 0; 
        } 
        photos = new Image [count+1]; 
        String prefix = getParameter("Prefix"); 
        for(int i = 1; i < count+1 ; i++) { 
            String parName = prefix + i, 
                   fileName = getParameter(parName); 
            photos[i] = getImage(where, fileName); 
            tracker.addImage(photos[i], i); 
        } 
        int failCount = 0; 
        for(int i = 1; i < count+1 ; i++) { 
            try { 
                tracker.waitForID(i); 
            } 
            catch(InterruptedException e) { 
            } 
            if(tracker.isErrorID(i)) 
                failCount++; 
        } 
        if(failCount != 0) { 
            showStatus("No. of failures: " + failCount); 
        } 
        width  = photos[1].getWidth(this); 
        height = photos[1].getHeight(this); 
    } 
    public void paint(Graphics gDC) 
    { 
        Thread thisThread = Thread.currentThread(); 
        for(int i = 1; i < count+1 ; i++) { 
            gDC.drawImage(photos[i], width*(i-1), 0,  
                          width, height,  
                          Color.black, this); 
            try { 
                thisThread.sleep(200); 

background image

 

143 

            } 
            catch(InterruptedException e) { 
            } 
        } 
    } 

 
Sprawdza sięże obrazy zostały pomyślnie załadowane, a dopiero po tym przystępuje się do ich wyświetlenia. 
 
 
_________________________________________________________________________________________ 

Rozkłady 

 
 
Sposób  rozmieszczenia  komponentów  w  pojemniku:  w  aplecie,  w  panelu  albo  w  ramce  określa  zarządca 
rozkładu
  (Layout  Manager).  Do  najczęściej  używanych  należą  zarządcy  FlowLayout,  BorderLayout  i 
GridLayout
 
W  chwili  utworzenia  pojemnika  jest  mu  przypisywany  zarządca  domyślny (np.  dla apletu  FlowLayout, a  dla 
ramki  BorderLayout).  Nic  jednak  nie  stoi  na  przeszkodzie,  aby  zarządca  został  zmieniony  na  dowolny  inny, 
także już po załadowaniu pojemnika. 
 
Rozmieszczenie  komponentów  w  pojemniku  może  się  odbywać  bez  udziału  zarządcy.  W  takim  wypadku 
położenie  i  rozmiar  każdego  z  komponentów  należy  określić  jawnie,  za  pomocą  metod  reshape  albo resize 
move klasy Component
 
 

public void resize(int width, int height) 

Zmiana rozmiarów komponentu na podane. 
 

public synchronized void reshape(int x, int y,  
                                 int width, int height) 

Zmiana położenia i rozmiarów komponentu na podane. 
 

public void move(int x, int y) 

Zmiana położenia lewego-górnego narożnika komponentu na podane. 
 
 
Następujący aplet, pokazany podczas wykonania na ekranie Bez zarządcy, nie posługuje się zarządcą rozkładu. 
 
Ekran Bez zarządcy 

### null.gif 

 

<applet code=Master.class width=300 height=100> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public  
class Master extends Applet { 
    public void init() 
    { 
        Panel panel = nullLayoutPanel(); 
        panel.resize(200, 200); 
        setLayout(null); 
        add(panel); 
    } 
    Panel nullLayoutPanel() 
    { 
        Panel panel = new Panel(); 
        panel.setLayout(null); 
        Button red   = new Button("Red"), 

background image

 

144 

               green = new Button("Green"), 
               blue  = new Button("Blue"); 
        TextField cell = new TextField("Hello"); 
 
        panel.add(red); 
        panel.add(green); 
        panel.add(blue); 
        panel.add(cell); 
 
        red.reshape(0, 0, 60, 30); 
        green.reshape(70, 0, 60, 30); 
        blue.reshape(140, 0, 60, 30); 
        cell.reshape(0, 40, 100, 30); 
 
        return panel; 
    } 

 
 

FlowLayout  

 
Rozkład  ciągły  (FlowLayout)  polega  na  rozmieszczaniu  komponentów  jeden-za-drugim.  Jeśli  zestaw 
komponentów  nie  mieści  się  w  wierszu  pojemnika,  to  kolejny  komponent  jest  przenoszony  do  następnego 
wiersza. 
 
 

public FlowLayout() 
public FlowLayout(int align) 
public FlowLayout(int align, int hGap, int vGap) 

Konstruktory  określają  właściwości  rozkładu,  w  tym  sposób  wyrównania  komponentów  (align
FlowLayout.LEFT,  FlowLayout.RIGHT,  FlowLayout.CENTER)  oraz  poziomy  (hGap)  i  pionowy  (vGap
odstęp między komponentami. 
 
 
Na ekranie Rozkład ciągły pokazano aplet, w którym zastosowano rozkład FlowLayout
 
Ekran Rozkład ciągły 

### flow.gif 

 

<applet code=Master.class width=160 height=80> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public  
class Master extends Applet { 
    public void init() 
    { 
        Panel panel = flowLayoutPanel(); 
        add(panel); 
    } 
    Panel flowLayoutPanel() 
    { 
        Panel panel = new Panel(); 
        panel.setLayout(new FlowLayout(FlowLayout.RIGHT)); 
        panel.add(new Button("Greet")); 
        panel.add(new Button("Clear")); 
        return panel; 
     } 

 
Przyciski Greet Clear wyrównano prawostronnie w wierszu pulpitu. 
 
 

background image

 

145 

BorderLayout  

 
Rozkład  brzegowy  (BorderLayout)  polega  na  rozmieszczeniu  komponentów  na  obrzeżach  i  w  środku 
pojemnika. Obrzeża mają nazwy stron świata: WestEastNorthSouth. Środek pojemnika ma nazwę Center
 
 

public Borderlayout() 
public BorderLayout(int hGap, int vGap) 

Konstruktory  określają  właściwości  rozkładu.  Drugi  z  nich  dodatkowo:  poziomy  (hGap)  i  pionowy  (vGap
odstęp między komponentami. 
 
 
Na ekranie Rozkład brzegowy pokazano aplet, w którym zastosowano rozkład BorderLayout
 
 
Ekran Rozkład brzegowy 

### border.gif 

 

<applet code=Master.class width=400 height=80> 
</applet> 
============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public  
class Master extends Applet { 
    public void init() 
    { 
        Panel panel = borderLayoutPanel(); 
        add(panel); 
    } 
    Panel borderLayoutPanel() 
    { 
        Panel panel = new Panel(); 
        panel.setLayout(new BorderLayout()); 
        panel.add("East",   new Button("Greet")); 
        panel.add("West",   new Button("Clear")); 
        panel.add("North",  new TextField(8)); 
        panel.add("Center", new Label("The Center")); 
        panel.add("South",  new TextArea()); 
        return panel; 
    } 

 
 

GridLayout 

 
Rozkład  siatkowy  (GridLayout)  polega  na  rozmieszczeniu  komponentów  w  układzie  prostokątnej  siatki  o 
podanych rozmiarach. Wszystkie jej klatki mają identyczne rozmiary. 
 
 

public GridLayout(int rows, int cols) 
public GridLayout(int rows, int cols, int hGap, int vGap) 

Konstruktory określają liczbę wierszy (rows) i kolumn (cols) rozkładu oraz poziomy (hGap) i pionowy (vGap
odstęp między komponentami. 
 
 
Na ekranie Rozkład siatkowy pokazano aplet, w którym zastosowano rozkład GridLayout
 
Ekran Rozkład siatkowy 

### grid.gif 

 

<applet code=Master.class width=240 height=160> 

background image

 

146 

</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public  
class Master extends Applet { 
    public void init() 
    { 
        Panel panel = gridLayoutPanel(); 
        add(panel); 
    } 
    Panel gridLayoutPanel() 
    { 
        Panel panel = new Panel(); 
        panel.setLayout(new GridLayout(4, 4, 10, 10)); 
        for(int k = 0, row = 0; row < 4 ; row++) 
            for(int col = 0; col < 4 ; col++) 
            if(k++ == 0) 
                panel.add(new Button("Clear")); 
            else 
                panel.add(new Button("Greet" + (k-1))); 
         return panel; 
    } 

 
 
_________________________________________________________________________________________ 

Sterowniki 

 
Sterownikami  są  komponenty  rozmieszczane  w  najgłębszych  pojemnikach.  Typowymi  sterownikami  są: 
etykieta  (Label),  przycisk  (Button),  klatka  (TextField),  notatnik  (TextArea)  i  płótno  (Canvas).  Etykieta  i 
notatnik są sterownikami biernymi (nie mogą obsługiwać zdarzeń). 
 
 

Label     

 
Etykieta  jest  sterownikiem  umożliwiającym  umieszczenie  w  pojemniku  dowolnie  sformatowanego  napisu
Napis  może  być  wyrównany:  lewostronnie  (Label.LEFT),  prawostronnie  (Label.RIGHT)  albo  środkująco 
(Label.CENTER). 
 
Na ekranie Etykiety pokazano aplet zawierający sterowniki Label
 
Ekran Etykiety 

### label.gif 

 

<applet code=Master.class width=100 height=100> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    public void init() 
    { 
        setLayout(new BorderLayout()); 
        Label left = new Label("Left"); 
        Label right = new Label("Right", Label.RIGHT); 
        add("North", left); 
        add("South", right); 
        add("Center", new Label("Center", Label.CENTER)); 
    } 

background image

 

147 

 
 

Button  

 
Przycisk  jest  sterownikiem,  którego  naciśnięcie  (kliknięcie  przyciskiem  myszki)  może  być  obsłużone,  co 
zazwyczaj  powoduje  wykonanie  akcji.  Zaleca  się,  aby  rodzaj  akcji  odpowiadał  napisowi  wykreślonemu  na 
przycisku. 
 
Uwaga:  Po  wygenerowaniu  zdarzenia  ACTION_EVENT  opis  przycisku  jest  dostępny  poprzez 
(String)evt.arg
 
Na ekranie Przyciski pokazano aplet zawierający sterowniki Button
 
Ekran Przyciski 

### button.gif 

 

<applet code=Master.class width=300 height=160> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
import java.util.*; 
 
public 
class Master extends Applet { 
    private Panel panel = new Panel(); 
    Label target = new Label("", Label.CENTER); 
    public void init() 
    { 
        panel.setLayout( 
                  new FlowLayout(FlowLayout.RIGHT, 10, 3) 
              ); 
        String java = "Java is very easy to learn"; 
        StringTokenizer phrase = new StringTokenizer(java); 
        while(phrase.hasMoreTokens())  
            panel.add(new MyButton(phrase.nextToken(), target)); 
        setLayout(new BorderLayout()); 
        add("Center", panel); 
        add("South", target); 
        Font font = new Font("Arial", Font.BOLD, 60);         
        target.setFont(font); 
    } 

 
class MyButton extends Button { 
    Label target; 
    MyButton(String label, Label target) 
    { 
        super(label); 
        this.target = target; 
    } 
    public boolean action(Event evt, Object arg) 
    { 
        target.setText((String)arg); 
        return true; 
    } 

 
Każde ze słów frazy  
 

 

Java is very easy to learn 

 
jest wyświetlane na osobnym przycisku.  
 
Naciśnięcie  przycisku  opatrzonego  wybranym  słowem  (np.  easy)  powoduje  wyświetlenie  go  w  dolnej  części 
apletu. 

background image

 

148 

 
 

TextField 

 
Klatka  jest  sterownikiem,  do  którego  można  wprowadzić  tekst.  Podczas  wprowadzania  tekstu  można 
posługiwać się klawiszami kierunkowymi oraz klawiszami Del i Backspace
 
Uwaga:  Po  naciśnięciu  klawisza  Enter  jest  generowane  zdarzenie  ACTION_EVENT.  Podczas  obsługiwania 
go zawartość klatki jest dostępna poprzez zmienną (String)evt.arg
 
Na ekranie Klatki pokazano aplet zawierający sterowniki TextField
 
Ekran Klatki 

### field.gif 

 

<applet code=Master.class width=240 height=80> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    public void init() 
    { 
        TextField firstName = new TextField(20), 
                  lastName  = new TextField(20); 
        Label firstNameLabel =  
                  new Label("First name: ", Label.RIGHT), 
              lastNameLabel =  
                  new Label("Last name: ",  Label.RIGHT); 
        setLayout(new FlowLayout()); 
        add(firstNameLabel); 
        add(firstName); 
        add(lastNameLabel); 
        add(lastName); 
    } 
    public boolean action(Event evt, Object arg) 
    { 
        String entry = arg.toString(); 
        ((TextField)evt.target).setText(entry.toUpperCase()); 
        return true; 
    } 

 
Po naciśnięciu klawisza Enter następuje zmiana liter wprowadzonych do klatki z małych na duże. 
 
 

TextArea 

 
Notatnik  jest  sterownikiem  implementującym  prosty  edytor.  Edytor  jest  wyposażony  w  suwaki.  Czynne  są 
klawisze  strzałkowe  oraz  Enter,  Backspace  i  Delete.  Notatnik  reaguje  tylko  na  naciśnięcia  klawiszy  oraz  na 
czynności wykonane za pomocą myszki (zmienna evt.arg nie ma interpretacji). 
 
Metody  appendText,  insertText  i  replaceText  umożliwiają  przetwarzanie  tekstu  wprowadzonego  do 
notatnika. 
 
 

public void appendText(String str) 

Metoda dokleja łańcuch str na końcu notatnika. 
 

public void insertText(String str, int pos) 

Metoda wstawia łańcuch str przed znakiem na pozycji pos notatnika. 

background image

 

149 

 

public void replaceText(String str, int f, int t) 

Metoda zastępuje wycinek znaków notatnika między jego pozycjami t, łańcuchem str
 
 
Na ekranie Notatnik pokazano aplet zawierający sterownik TextArea
 
Ekran Notatnik 

### area.gif 

 

<applet code=Master.class width=300 height=120> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    TextArea textArea; 
    public void init() 
    { 
        String aProgram =  
            "  public                        \n" + 
            "  class Simplest {              \n" + 
            "      public static             \n" + 
            "      void main(String args[])  \n" + 
            "      {                         \n" + 
            "      }                         \n" + 
            "  }                             \n"; 
 
        int width  = Integer.parseInt(getParameter("width")), 
            height = Integer.parseInt(getParameter("height")); 
        textArea = new TextArea(aProgram, 100, 40); 
        setLayout(null); 
        add(textArea); 
        textArea.reshape(0, 0, width, height); 
    } 
    public boolean handleEvent(Event evt) 
    { 
        if(evt.id == Event.KEY_ACTION && 
            evt.key == Event.F5 && 
            (evt.modifiers & Event.CTRL_MASK) != 0) { 
            textArea.insertText("  final\n", 0); 
            return true; 
        } 
        return false; 
    } 


 
Wykonanie programu powoduje wyświetlenie notatnika zawierającego kod najkrótszej aplikacji. Po naciśnięciu 
klawisza Ctrl-F5, przed pierwszy wiersz tego programu jest wstawiany napis  
 

 

final 

 
 

Canvas 

 
Płótno  jest  sterownikiem,  pozbawionym  predefiniowanej  semantyki  (jak  na  przykład  przycisk).  Na  płótnie 
można umieszczać dowolne sterowniki. Można również sporządzać na nim wykresy
 
Uwaga: Płótno reaguje tylko na naciśnięcia klawiszy oraz na czynności wykonane za pomocą myszki (zmienna 
evt.arg nie ma interpretacji). 
 
Na ekranie Płótna pokazano następujący aplet zawierający sterowniki Canvas
 

background image

 

150 

Ekran Płótna 

### canvas.gif 

 

<applet code=Master.class width=180 height=180> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    public void init() 
    { 
        setLayout(new GridLayout(5, 5)); 
        for(int shade = 0, row = 0; row < 5 ; row++) 
            for(int col = 0; col < 5 ; col++) { 
                Canvas canvas = new RedCircle(shade += 10); 
                add(canvas); 
            } 
    } 

 
class RedCircle extends Canvas { 
    int shade; 
    RedCircle(int shade) 
    { 
        this.shade = shade; 
    } 
    public void paint(Graphics gDC) 
    { 
        gDC.setColor(new Color(shade, 0, 0)); 
        gDC.fillOval(0, 0, 35, 35); 
        gDC.setColor(Color.white); 
        gDC.fillOval(0, 0, 15, 15); 
    } 
    public boolean mouseUp(Event evt, int x, int y) 
    { 
        Graphics gDC = getGraphics(); 
        gDC.drawLine(0, 0, x, y); 
        return true; 
    } 

 
Wykonanie  programu  powoduje  m.in.  wyświetlenie  zestawu  okręgów  zabarwionych  na  czerwono,  każdy  w 
innym odcieniu. 
 
 

MenuItem 

 
Polecenie jest sterownikiem umożliwiającym wyposażenie ramki w menu
 
Menu  jest  komponentem  klasy  Menu,  a  polecenie  menu  jest  sterownikiem  klasy  MenuItem.  Wybieranie 
poleceń odbywa się przez kliknięcie. 
 
Uwaga:  Po  wygenerowaniu  zdarzenia  kategorii  ACTION_EVENT  etykieta  polecenia  menu  jest  dostępna 
poprzez zmienną (String)evt.arg
 
Na ekranie Menu pokazano aplet, który posługuje się odrębną ramką zawierającą sterowniki MenuItem
 
Ekran Menu 

### menuitem.gif 

 

<applet code=Master.class width=180 height=180> 
</applet> 
=============================================== 

 

background image

 

151 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    Frame greetFrame; 
    Label label = new Label("Choose greeting from menu"); 
    public void init() 
    { 
        greetFrame = new MyFrame("Greet", label); 
        greetFrame.resize(200, 200); 
        greetFrame.show(); 
 
        setLayout(new FlowLayout()); 
        add(new Button("Show / Hide")); 
 
        int width  = Integer.parseInt(getParameter("width")), 
            height = Integer.parseInt(getParameter("height")); 
        greetFrame.resize(width, height); 
        MenuBar menuBar = new MenuBar(); 
        greetFrame.setMenuBar(menuBar); 
 
        Menu fileMenu = new Menu("File"); 
        fileMenu.add(new MenuItem("-"));   // separator 
        fileMenu.add(new MenuItem("Exit")); 
        menuBar.add(fileMenu); 
 
        Menu greetMenu = new Menu("Greet"); 
        greetMenu.add(new MenuItem("Say Hello")); 
        greetMenu.add(new MenuItem("Say Hi")); 
        menuBar.add(greetMenu); 
 
        Menu helpMenu = new Menu("Help"); 
        helpMenu.add(new MenuItem("About")); 
        menuBar.add(helpMenu); 
        menuBar.setHelpMenu(helpMenu);     // wyrównanie w prawo 
 
        greetFrame.setLayout(new BorderLayout()); 
        greetFrame.add("South", label); 
    } 
    public boolean handleEvent(Event evt) 
    { 
        if(evt.arg.equals("Show / Hide")) { 
            if(greetFrame.isShowing()) 
                greetFrame.hide(); 
            else 
                greetFrame.show(); 
            return true; 
        } 
        return false; 
    } 

 
class MyFrame extends Frame { 
    Label label; 
    MyFrame(String caption, Label label) 
    { 
        super(caption); 
        this.label = label; 
    } 
    public boolean handleEvent(Event evt) 
    { 
        if(evt.id == Event.WINDOW_DESTROY) 
            dispose();        // obsługa klikni

ę

cia na x 

        if(evt.arg.equals("Say Hello")) { 
            label.setText("Hello Jan B."); 
            return true; 
        } else if(evt.arg.equals("Say Hi")) { 
            label.setText("Hi Jan B."); 
            return true; 
        } else if(evt.arg.equals("Exit")) { 
            dispose(); 
            return true; 

background image

 

152 

        } else if(evt.arg.equals("About")) { 
            label.setText("Copyright JanB."); 
            return true; 
        } 
        return false; 
    } 

 
Program ilustruje sposób reagowania na polecenia menu, w które wyposażono ramkę utworzoną przez aplet. 
 
 
_________________________________________________________________________________________ 

Pojemniki 

 
Pojemnik  jest  komponentem,  który  może  zawierać  w  sobie  inne  komponenty:  pojemniki  i  sterowniki
Głębokość  zagnieżdżania  komponentów  w  pojemnikach  nie  jest  ograniczona.  Na  każdym  poziomie 
zagnieżdżenia  może  być  użyty  inny  zarządca  rozkładu.  Poza  apletem,  typowymi  pojemnikami  są:  panel 
(Panel), ramka (Frame) i dialog (FileDialog). 
 
 

Panel 

 
Panel jest pojemnikiem o domyślnym rozkładzie FlowLayout. Panel zawierający sterowniki jest traktowany jak 
pojedynczy komponent i jako taki może być umieszczony w innym pojemniku. 
 
Następujący program, pokazany podczas wykonywania na ekranie Panele, tworzy dwa panele i rozmieszcza je 
w górnej i dolnej części apletu z rozkładem brzegowym. 
 
Ekran Panele 

### panels.gif 

 

<applet code=Master.class width=160 height=80> 
</applet> 
============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    Label label; 
    Button tom, bob; 
    public void init() 
    { 
        Panel panel = new Panel(); 
        panel.add(tom = new Button("Tom")); 
        panel.add(bob = new Button("Bob")); 
        setLayout(new BorderLayout()); 
        add("North", panel); 
        add("South", panel); 
        add("Center", label = new Label("Press a Button")); 
     } 
     public boolean action(Event evt, Object arg) 
     { 
         if(evt.target == tom) 
             label.setText("Hello Bob"); 
         else if(evt.target == bob) 
             label.setText("Hello Tom"); 
         else 
             return false; 
         return true; 
     } 
}     

 
Kliknięcie przycisku Tom powoduje wykreślenie napisu 

background image

 

153 

 

 

Hello Bob 

 
a kliknięcie przycisku Bob powoduje wykreślenie napisu 
 

 

Hello Tom 

 
 

Frame 

 
Ramka  jest  pojemnikiem  z  domniemanym  zarządcą  rozkładu  BorderLayout.  Ramka  reaguje  na  zdarzenia 
związane z operacjami wykonywanymi za pomocą myszki i klawiatury
 
Następujący program, pokazany podczas wykonania na ekranie Ramki, wyświetla 10 ramek stowarzyszonych z 
apletem, a w każdej z nich wykreśla jedną cyfrę. 
  
Ekran Ramki 

### frames.gif 

 

<applet code=Master.class width=160 height=80> 
</applet> 
============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    public void init() 
    { 
        for(int i = 0; i < 10 ; i ++) { 
            Frame frame = new MyFrame(i); 
            frame.reshape(10 * (i+1), 10 * (i+1), 100, 100); 
            frame.show(); 
        } 
    } 

 
class MyFrame extends Frame { 
    int digit; 
    Font font = new Font("TimesRoman", Font.BOLD, 60); 
    MyFrame(int digit) 
    { 
        this.digit = digit; 
    } 
    public void paint(Graphics gDC) 
    { 
        gDC.setFont(font); 
        gDC.drawString("" + digit, 50, 50); 
    } 
    public boolean handleEvent(Event evt) 
    { 
        if(evt.id == Event.WINDOW_DESTROY) 
            dispose(); 
        return false; 
    }     

 
 

Dialog 

 
Dialog  plikowy  jest  pojemnikiem  ułatwiającym  załadowanie  i  zachowanie  pliku.  Umożliwia  on  wyświetlenie 
okna z pytaniami o stację, ścieżkę i plik
 
Obiekty dialogowe są tworzone za pomocą konstruktorów klasy FileDialog, a wyświetlenie dialogu odbywa się 
za pomocą metody show

background image

 

154 

 
Operacje  na  zmiennych  obiektu  dialogowego  są  wykonywane  m.in.  za  pomocą  metod  getFile,  getDirectory 
oraz setFile setDirectory
 
 

public FileDialog(Frame parent, String caption, int mode) 

Konstruktor tworzy dialog załadowania albo zachowania (SAVE). Rodzaj dialogu można określić za pomocą 
argumentu mode (FileDialog.LOAD, albo FileDialog.SAVE). 
 

public FileDialog(Frame parent, String caption) 

Konstruktor tworzy dialog załadowania (LOAD). 
 

public void show() 

Metoda wyświetla dialog opisany przez obiekt dialogowy. 
 

public String getFile() 

Metoda zwraca nazwę pliku. 
 

public String getDirectory() 

Metoda zwraca nazwę katalogu. 
 

public void setFile(String file) 

Metoda ustawia nazwę pliku. 
 

public void setDirectory(String dir) 

Metoda ustawia nazwę katalogu. 
 
 
Następujący  aplet,  pokazany podczas wykonania na ekranie Dialog plikowy, ilustruje zasadę posługiwania się 
dialogiem plikowym. 
 
Ekran Dialog plikowy 

### dialog.gif 

 

<applet code=Master.class width=160 height=80> 
</applet> 
============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    Frame frame = new Frame("Parent"); 
    String dir, file; 
    public void init() 
    { 
        frame.show(); 
        add(new Button("Load")); 
        add(new Button("Save")); 
    } 
    public boolean action(Event evt, Object arg) 
    { 
        if(evt.target instanceof Button) { 
            FileDialog fileDialog = null; 
            if(arg.equals("Save")) { 
                fileDialog = 
                    new FileDialog(frame, "Save", 
                                   FileDialog.SAVE); 
            } 
            if(arg.equals("Load")) { 
                fileDialog = 
                    new FileDialog(frame, "Load", 
                                   FileDialog.LOAD); 
            } 
            fileDialog.show(); 

background image

 

155 

            dir  = fileDialog.getDirectory(); 
            file = fileDialog.getFile(); 
            repaint(); 
            return true; 
        } 
        return false; 
    } 
    public void paint(Graphics gDC) 
    { 
        gDC.drawString("Dir: "  + dir,  10, 50); 
        gDC.drawString("File: " + file, 10, 70); 
    } 

 
Program wyświetla nazwę katalogu i pliku, wybrane w oknie dialogu plikowego. 
 
 
_________________________________________________________________________________________ 

Kontrolki 

 
Kontrolką jest sterownik stanowiący kompozycję komponentów. Komponenty zaleca się wyposażyć w metody 
minimumSize preferredSize, a na rzecz kontrolki wywołać metodę validate. W przeciwnym razie może się 
zdarzyć, że elementy kontrolki nie będą właściwie wykreślane. 
 
 

public Dimension minimumSize() 

Metoda określa minimalny rozmiar komponentu. 
 

public Dimension preferredSize() 

Metoda określa ulubiony rozmiar komponentu. 
 

public void validate() 

Metoda powoduje ponowne przeliczenie rozmiarów i położenia komponentu. 
 
 
Uwaga:  Jedynym  sposobem  dodania  metody  minimumSize  i  preferredSize  do  klasy  predefiniowanej  (np. 
Button), jest utworzenie jej podklasy
 
Na przykład 
 

class MyButton extends Button { 
    MyButton(String caption) 
    { 
        super(caption); 
    } 
    public Dimension minimumSize() 
    { 
        return new Dimension(20, 20); 
    } 
    // ... 

 
Klasa MyButton może być używana w taki sam sposób jak klasa Button.  
 
Od klasy Button różni się własną metodą minimumSize
 
 
Następujący  aplet,  pokazany  podczas  wykonywania  na  ekranie  Kontrolka,  ilustruje  sposób  wykorzystania 
własnego  sterownika  (zdefiniowanego  za  pomocą  klas  Display  i  Key),  z  trójwymiarowymi  przyciskami 
umożliwiającymi wprowadzanie liczb całkowitych. 
 
Uwaga:  Przycisk  S  kontrolki  oprogramowano  w  taki  sposób,  aby  umożliwiał  wyeksportowanie  liczby  ze 
sterownika (klasa Display definiuje sterownik-wyświetlacz, a klasa Key sterownik-przycisk). 

background image

 

156 

 
Ekran Kontrolka 

### display.gif 
 
<applet code=Master.class width=130 height=180> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    private Display theDisplay; 
    private TextField theCell; 
    public void init() 
    { 
        setLayout(new BorderLayout()); 
        theCell = new TextField("Ready"); 
        theDisplay = new Display(); 
        add("North", theCell); 
        add("East", theDisplay); 
    } 
    public boolean mouseUp(Event evt, int x, int y) 
    { 
        int value = theDisplay.getValue(); 
        theCell.setText(String.valueOf(value)); 
        return true; 
    } 

 
Aplet tworzy klatkę tekstową i wyświetlacz z przyciskami. Procedura mouseUp rozpoznaje naciśnięcie przycisku 
S wy
świetlacza i obsługuje je w taki sposób, że liczbę znajdującą się w wyświetlaczu wstawia do klatki. 
 
 

Klasa Display 

 
Klasa  Display  definiuje  wyświetlacz  z  przyciskami.  Zwolnienie  przycisku  S  nie  powoduje  obsłużenia  tego 
zdarzenia (po naciśnięciu przycisku S metoda mouseUp zwraca false). Stwarza to możliwość obsłużenia go w 
pojemniku kontrolki. 
 

class Display extends Panel { 
    private int value = 0; 
    private Panel display = new Panel(); 
    private TextField number = new TextField("0"); 
    private Panel buttons = new Panel(); 
    private static String labels = "7894561230 S"; 
    private char lastKey = '0'; 
 
    Display() 
    { 
        display.setLayout(new BorderLayout()); 
        buttons.setLayout(new GridLayout(4, 3)); 
        for(int i = 0; i < 12 ; i++) { 
            char chr = labels.charAt(i); 
            Key key = new Key(this, chr); 
            buttons.add(key); 
        } 
        display.add("North",  number); 
        display.add("Center", buttons); 
        add(display); 
    } 
    public void setLastKey(char key) 
    { 
        lastKey = key; 
    } 
    int getValue() 
    { 
        return value; 

background image

 

157 

    } 
    void setValue(int value) 
    { 
        this.value = value; 
        number.setText(new String("" + value)); 
    } 
    public boolean handleEvent(Event evt) 
    { 
        if(evt.id == Event.MOUSE_UP) 
            return mouseUp(evt, evt.x, evt.y); 
        return true; 
    } 
    public boolean mouseUp(Event evt, int x, int y) 
    { 
        switch(lastKey) { 
            case ' ': 
                value = 0; 
                number.setText("0"); 
                return true; 
            case 'S': 
                return false; // nie obsłu

ż

ono, posłano dalej 

            default: 
                int digit = lastKey - '0'; 
                value = value * 10 + digit; 
                number.setText(String.valueOf(value)); 
                return true; 
        } 
    } 

 

 

Klasa Key 

 
Klasa  Key  definiuje  trójwymiarowy  przycisk  wyświetlacza.  Efekt  trójwymiarowości  uzyskano  przez 
wykreślenie łuków w kolorze czarnym i białym. Wybrany rozmiar przycisków określa metoda preferredSize
 
Naciśnięcie  przycisku  wyświetlacza  powoduje  wykreślenie  go  w  postaci  wciśniętej.  Zwolnienie  przycisku 
powoduje  zapamiętanie  wciśniętego  przycisku,  ale  bez  obsłużenia  tego  zdarzenia  (metoda  mouseUp  zwraca 
false). Umożliwia to obsłużenie tego zdarzenia w pojemniku-wyświetlaczu Display
 

class Key extends Panel { 
    Display display; 
    private char theKey; 
    private Color color; 
    private boolean mouseIsDown = false; 
 
    Key(Display display, char theKey) 
    { 
        this.display = display; 
        this.theKey = theKey; 
    } 
    public boolean mouseDown(Event evt, int x, int y) 
    { 
        mouseIsDown = true; 
        repaint(); 
        return true; 
    } 
    public boolean mouseUp(Event evt, int x, int y) 
    { 
        mouseIsDown = false; 
        repaint(); 
        display.setLastKey(theKey); 
        return false; 
    } 
    public void paint(Graphics gDC) 
    { 
        Rectangle bounds = bounds(); 
        int w = bounds.width, 
            h = bounds.height; 
        Color oldColor = gDC.getColor(); 

background image

 

158 

        drawOval(gDC, Color.red, 0, 0, w, h); 
        int x = (w /= 2) / 2, 
            y = (h /= 2) / 2; 
        drawOval(gDC, Color.green, x, y, w, h); 
        gDC.setColor(oldColor); 
        drawLabel(gDC, theKey, x, y, w, h); 
    } 
    void drawOval(Graphics gDC, Color color, 
                  int x, int y, int w, int h) 
    { 
        gDC.setColor(color); 
        gDC.fillOval(x, y, w, h); 
        color = mouseIsDown ? Color.white : Color.black; 
        gDC.setColor(color); 
        gDC.drawArc(x, y, w, h, 45, -180); 
        color = mouseIsDown ? Color.black : Color.white; 
        gDC.setColor(color); 
        gDC.drawArc(x, y, w, h, 45, +180); 
    } 
    public void drawLabel(Graphics gDC, char label, 
                          int x, int y, int w, int h) 
    { 
        Font font = new Font("Arial", Font.BOLD, 12); 
        gDC.setFont(font); 
        FontMetrics metrics = gDC.getFontMetrics(); 
        int as = metrics.getAscent(), 
            ww = metrics.charWidth(label); 
        x += (w - ww) / 2; 
        y += (h + as) / 2; 
        gDC.drawString("" + label, x, y); 
    } 
    public Dimension preferredSize() 
    { 
        return new Dimension(30, 30); 
    } 
    public Dimension minimumSize() 
    { 
        return new Dimension(30, 30); 
    } 

 

 

Pośrednik-Obserwator-Kontroler 

 
Wykorzystując  bez  zmian  zdefiniowane  powyżej  klasy  Display  i  Key,  można  podać  uogólnione  rozwiązanie 
postawionego  problemu.  Stanowi  ono  ilustrację  metody  komunikowania  się  sterowników  znanej  pod  nazwą 
Pośrednik-Obserwator-Kontroler (Model-View-Controller).  Istotą metody  jest skonstruowanie klasy obiektów 
pośredniczących, które mogą być jednocześnie obserwowane i kontrolowane.  
 
Jeśli  obiekt-kontroler  dokona  modyfikacji  obiektu-pośrednika,  to  za  pomocą metody update zostanie o tym 
powiadomiony  obiekt-obserwator.  Kontroler  i  obserwator  nic  o  sobie  nie  wiedzą  i  komunikują  się  tylko 
poprzez pośrednika. Dzięki temu modyfikacja jednego z tych sterowników nie ma żadnego wpływu na drugi. 
Czyni to program prawdziwie modularnym i zmniejsza niebezpieczeństwo niekontrolowanego propagowania 
zmian kodu. 
 
Wykonanie następującego programu, w którym rolę pośrednikaobserwatora i kontrolera pełnią odpowiednio 
obiekty  klas  ObservableInt,  ObserverTextField  i  ControllerDisplay  ma  taki  sam  skutek  jak  uprzednio. 
Ponadto, wprowadzenie do klatki tekstowej pewnej liczby, powoduje przeniesienie jej do klatki wyświetlacza.  
 
W szczególności, gdyby metodę 
 

public void update(Observable obs, Object arg) 

    setValue(theInt.getInt());  

 
zastąpiono metodą 

background image

 

159 

 

public void update(Observable obs, Object arg) 

    return; 

 
to naciśnięcie klawisza Enter w obrębie klatki tekstowej anulowałoby przeniesienie liczby do wyświetlacza. 
 
Następujący  aplet,  pokazany  podczas  wykonywania  na  ekranie  Kontrolka  dla  koneserów,  ilustruje 
zastosowanie metody Pośrednik-Obserwator-Kontroler
  
Ekran Kontrolka dla koneserów 

### control.gif 
 
 

Przykład Kontrolka dla koneserów 
 

<applet code=Master.class width=130 height=180> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
import java.util.*; 
 
public 
class Master extends Applet { 
    private Display theDisplay; 
    private TextField theCell; 
    ObservableInt theInt = new ObservableInt(0); 
    public void init() 
    { 
        setLayout(new BorderLayout()); 
        theCell = new ObserverTextField(theInt, "Ready"); 
        theDisplay = new ControllerDisplay(theInt); 
        add("North", theCell); 
        add("East",  theDisplay); 
    } 

 
class ObservableInt extends Observable { 
    int theInt; 
    ObservableInt(int theInt) 
    { 
        this.theInt = theInt; 
    } 
    public synchronized int getInt() 
    { 
        return theInt; 
    } 
    public synchronized void setInt(int newInt) 
    { 
        if(newInt != theInt) { 
            theInt = newInt; 
            setChanged(); 
            notifyObservers(); 
        } 
    } 

 
class ObserverTextField  
      extends TextField  
      implements Observer { 
    ObservableInt theInt; 
    ObserverTextField(ObservableInt theInt, String string) 
    { 
        this.theInt = theInt; 
        theInt.addObserver(this); 
        setText(string); 
    } 

background image

 

160 

    public boolean action(Event evt, Object arg) 
    { 
        String string = (String)arg; 
        int value; 
        try { 
            value = Integer.parseInt(string); 
        } 
        catch(NumberFormatException e) { 
            setText("Not a Number"); 
            return true; 
        } 
        theInt.setInt(value); 
        return true; 
    } 
    public void update(Observable obs, Object arg) 
    { 
        setText("" + theInt.getInt()); 
    } 

 
class ControllerDisplay extends Display  
                        implements Observer { 
    ObservableInt theInt; 
    ControllerDisplay(ObservableInt theInt) 
    { 
        this.theInt = theInt; 
        theInt.addObserver(this); 
    } 
    public boolean handleEvent(Event evt) 
    { 
        if(!super.handleEvent(evt)) 
            theInt.setInt(getValue()); 
        return true; 
    } 
    public void update(Observable obs, Object arg) 
    { 
        setValue(theInt.getInt());  
    } 

 
 
_________________________________________________________________________________________ 

Dźwięki 

 
 
W  chwili  obecnej  są  rozpoznawane  tylko  pliki  dźwiękowe  w  formacie  AU.  W  celu  odtworzenia  dźwięków 
zarejestrowanych  w  innych  formatach  należy  dokonać  ich  konwersji.  Jednym  z  lepszych  produktów  do 
wykonania tego zadania jest program Wham dostępny w Internecie
 
Do  załadowania  pliku  dźwiękowego  i  utworzenia  identyfikującego  go  obiektu  klasy  AudioClip  służy  metoda 
getAudioClip, a do zarządzania plikami dźwiękowymi metody loopplay i stop wywoływane na rzecz takiego 
obiektu. 
 
 

public AudioClip getAudioClip(URL url, String fileName) 

Metoda ładuje plik dźwiękowy fileName, znajdujący się pod adresem url
 

public abstract void loop() 

Wywołanie metody loop powoduje cykliczne odtwarzanie podanego dźwięku. 
 

public abstract void play() 

Wywołanie metody play powoduje odegranie podanego dźwięku. 
 

public abstract void stop() 

Wywołanie metody stop powoduje zaniechanie dalszego odgrywania dźwięku. 
 

background image

 

161 

 
Następujący  aplet,  pokazany  podczas  wykonywania  na  ekranie  Melodie,  odgrywa  dwie  melodie:  jedną 
cyklicznie w tle (z pliku Gong.au) i drugą co 1 s (z pliku Ding.au). 
 
Ekran Melodie 

### melody.gif 

 

<applet code=Master.class width=100 height=100> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
import java.net.*; 
import java.util.*; 
 
public 
class Master extends Applet implements Runnable { 
    private AudioClip backGround; 
    private AudioClip foreGround; 
    Thread player = null; 
    Random rand = new Random(); 
    public void init() 
    { 
        URL from = getCodeBase(); 
        backGround = getAudioClip(from, "Gong.au"); 
        foreGround = getAudioClip(from, "Ding.au"); 
    } 
    public void start() 
    { 
        if(backGround != null) 
            backGround.loop(); 
        if(player == null) { 
            player = new Thread(this); 
            player.start(); 
        } 
    } 
    public void stop() 
    { 
        if(player != null) { 
            player.stop(); 
            player = null; 
        } 
        if(backGround != null) 
            backGround.stop(); 
    } 
    public void run() 
    { 
        while(player != null) { 
            if(foreGround != null) { 
                drawCircle(); 
                foreGround.play(); 
            } 
            try { 
                Thread.sleep(1000); 
            } 
            catch(InterruptedException e) { 
            } 
        } 
    } 
    int abs(int par) 
    { 
        return par < 0 ? -par : par; 
    } 
    void drawCircle() 
    { 
        int d = 10; 
        int x = rand.nextInt() % (100-d), 
            y = rand.nextInt() % (100-d); 
        Graphics gDC = getGraphics(); 
        gDC.drawOval(abs(x), abs(y), d, d); 

background image

 

162 

    } 

 
Przed  każdym  odegraniem  dźwięku  z  pliku  Ding.au  jest  wykreślane  małe  kółko.  Jego  położenie  na  pulpicie 
apletu jest przypadkowe
 
 
_________________________________________________________________________________________ 

Animacje 

 
Animacja  polega  na  sukcesywnym  generowaniu  albo  odtwarzaniu  sekwencji  kadrów.  Generowanie  i 
wyświetlanie kadrów odbywa się zazwyczaj w odrębnym wątku. 
 
Program  apletu  realizującego  animację  (aż  do  kliknięcia  lewym  przyciskiem  myszki),  przybiera  zazwyczaj 
postać  
 

import java.applet.*; 
import java.awt.*; 
 
 
public 
class Master extends Applet  
             implements Runnable { 
    // ... 
 
    Thread engine; 
 
    boolean stopped = false; 
 
    // ... 
 
    public void init() 
    { 
        // ... przygotowanie pierwszego kadru 
    } 
 
    public void start() 
    { 
        if(engine == null) { 
                     // utworzenie w

ą

tku 

                     // implementowanego przez  
                     // metod

ę

 run 

            engine = new Thread(this); 
                     // aktywowanie w

ą

tku 

            engine.start(); 
        } 
    } 
 
    public void stop() 
    { 
        if(engine != null && engine.isAlive()) 
                     // zniszczenie w

ą

tku 

            engine.stop(); 
        engine = null; 
    } 
 
    public void paint(Graphics gDC) 
    { 
        // ... wykre

ś

lenie kadru 

    } 
 
    public boolean mouseUp(Event evt,  
                           int x, int y) 
    { 
        return stopped = true; 
    } 
 
    public void run() 
    { 

background image

 

163 

        while(!stopped) { 
            // ... przygotowanie kolejnego kadru 
            repaint(); 
            try { 
                engine.sleep(100); 
            } 
            catch(InterruptedException e) { 
            } 
            // ...  
        } 
    } 

 
 

Generowanie 

 
Następujący aplet, pokazany podczas wykonywania na ekranie Generowanie kadrów, powoduje wyświetlenie 
małego okręgu poruszającego się po elipsie. Obraz okręgu, w jego kolejnych położeniach, jest generowany w 
odstępach co 100 ms. 
 
Ekran Generowanie kadrów 

### circle.gif 

 

<applet code=Master.class width=160 height=160> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet  
             implements Runnable { 
    static final int r = 5; 
    static final double Pi = Math.PI; 
    static final int steps = 24; 
    static final double step = 2 * Pi/steps; 
    Thread rotor; 
    boolean stopped = false; 
    static double startAngle = Pi/4; 
    int width, height; 
    double xSubAxis, ySubAxis, angle; 
 
    public void init() 
    { 
        width  = Integer.parseInt( 
                             getParameter("width") 
                         ); 
        height = Integer.parseInt( 
                             getParameter("height") 
                         ); 
        xSubAxis = width /2 - 2*r; 
        ySubAxis = height/2 - 2*r; 
    } 
 
    public void start() 
    { 
        if(rotor == null) { 
            rotor = new Thread(this); 
            rotor.start(); 
        } 
    } 
 
    public void stop() 
    { 
        if(rotor != null && rotor.isAlive()) 
            rotor.stop(); 
        rotor = null; 
    } 
 

background image

 

164 

    public void paint(Graphics gDC) 
    { 
        int width  = (int)(2 * xSubAxis), 
            height = (int)(2 * ySubAxis); 
        gDC.drawOval(r, r, width, height); 
        int x = (int)(r + xSubAxis *  
                     (1 + Math.cos(angle))), 
            y = (int)(r + ySubAxis *  
                     (1 - Math.sin(angle))); 
        gDC.drawOval(x - r, y - r, 2*r, 2*r); 
    } 
 
    public boolean mouseUp(Event evt,  
                           int x, int y) 
    { 
        return stopped = true; 
    } 
 
 
    public void run() 
    { 
        while(!stopped) { 
            repaint(); 
            try { 
                rotor.sleep(100); 
            } 
            catch(InterruptedException e) { 
            } 
            angle += step; 
        } 
    } 

 
 

Buforowanie 

 
Ponieważ każde wywołanie metody repaint powoduje wyczyszczenie pulpitu, więc prosta animacja odbywa się 
z niezbyt przyjemnym dla oka migotaniem obrazu.  
 
Można  temu  zaradzić,  stosując  buforowanie.  Polega  ono  na  przygotowaniu  obrazu  w  buforze  pamięci 
operacyjnej, a następnie rzuceniu zawartości bufora na ekran. 
 
Istotę buforowania wyjaśnia następujący wycinek programu 
 

      // okre

ś

lenie rozmiarów apletu 

Dimension d = size(); 
int w = d.width, h = d.height; 
 
      // utworzenie bufora w pami

ę

ci operacyjnej 

backBuffer = createImage(w, h); 
 
      // utworzenie wykre

ś

lacza zwi

ą

zanego z buforem 

Graphics gDC = backBuffer.getGraphics(); 
 
      // przygotowanie obrazu za pomoc

ą

 metody paint 

paint(gDC);   
 
      // utworzenie wykre

ś

lacza zwi

ą

zanego z pulpitem 

gDC = getGraphics(); 
 
      // skopiowanie bufora na pulpit 
gDC.drawImage(backBuffer, 0, 0, this); 

 
 
Następujący aplet, pokazany podczas wykonania na ekranie Buforowanie, wykorzystuje technikę buforowania 
do generowania kadrów. 
 
Ekran Buforowanie 

background image

 

165 

### buffer.gif 

 

<applet code=Master.class width=160 height=100> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet  
             implements Runnable { 
    static final int r = 5; 
    static final double Pi = Math.PI; 
    static final int steps = 24; 
    static final double step = 2 * Pi/steps; 
    Thread rotor; 
    boolean stopped = false; 
    static double startAngle = Pi/4; 
    int width, height; 
    double xSubAxis, ySubAxis, angle; 
    Image backBuffer; 
 
    public void init() 
    { 
        width  = Integer.parseInt( 
                             getParameter("width") 
                         ); 
        height = Integer.parseInt( 
                             getParameter("height") 
                         ); 
        xSubAxis = width /2 - 2*r; 
        ySubAxis = height/2 - 2*r; 
    } 
 
    public void start() 
    { 
        if(rotor == null) { 
            rotor = new Thread(this); 
            rotor.start(); 
        } 
    } 
 
    public void stop() 
    { 
        if(rotor != null && rotor.isAlive()) 
            rotor.stop(); 
        rotor = null; 
    } 
 
    public void paint(Graphics gDC) 
    { 
        int width  = (int)(2 * xSubAxis), 
            height = (int)(2 * ySubAxis); 
        gDC.drawOval(r, r, width, height); 
        int x = (int)(r + xSubAxis *  
                     (1 + Math.cos(angle))), 
            y = (int)(r + ySubAxis *  
                     (1 - Math.sin(angle))); 
        gDC.drawOval(x - r, y - r, 2*r, 2*r); 
    } 
 
    public void run() 
    { 
        while(!stopped) { 
            Dimension d = size(); 
            int w = d.width, h = d.height; 
            backBuffer = createImage(w, h); 
            Graphics gDC = backBuffer.getGraphics(); 
            paint(gDC);       // wykre

ś

lenie do bufora 

            gDC = getGraphics(); 
            gDC.drawImage(backBuffer, 0, 0, this); 

background image

 

166 

            try { 
                rotor.sleep(100); 
            } 
            catch(InterruptedException e) { 
            } 
            angle += step; 
        } 
    } 

 
W odróżnieniu od poprzedniego przykładu, tło wykresu jest białe
 
 

Odtwarzanie 

 
Następujący  aplet,  pokazany  podczas  wykonania  na  ekranie  Odtwarzanie  kadrów,  wyświetla  obracające  się 
ś

migło. Kolejne kadry animacji pochodzą z pliku Turbo.gif

 
Do  upewnienia  się,  że  animacja  rozpocznie  się  dopiero  po  przygotowaniu  kadrów,  wykorzystano  zarządcę 
mediów. Do wyświetlania kolejnych kadrów zastosowano technikę buforowania. 
 
Ekran Odtwarzanie kadrów 

### propel.gif 

 
Ekran Plik Turbo.gif 

### turbo.gif 

 

<applet code=Master.class width=160 height=160> 
<param name=Image value=Turbo.gif> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
import java.net.*; 
 
public 
class Master extends Applet 
             implements Runnable { 
    Thread engine; 
    boolean stopped = false; 
    MediaTracker tracker; 
    String imgFrames; 
    Image imageFrames; 
    Image backBuffer; 
    static final int frameSize = 160; 
    static final int frameCount = 8; 
    int pos = 0, xPos = 0; 
 
    public void init() 
    { 
        imgFrames = getParameter("Image"); 
                // utworzenie zarz

ą

dcy mediów 

        tracker = new MediaTracker(this); 
        URL codeBase = getCodeBase(); 
                // zainicjowanie pobrania obrazu 
        imageFrames = getImage(codeBase, imgFrames); 
                // powierzenie zarz

ą

dcy mediów 

                // nadzoru sprowadzenia obrazu 
        tracker.addImage(imageFrames, 1); 
        try { 
                // wstrzymanie wykonania apletu 
                // do chwili sprowadzenia obrazu 
            tracker.waitForID(1); 
        } 
        catch(InterruptedException e) { 
        } 
    } 

background image

 

167 

 
    public void start() 
    { 
        if(engine == null) { 
            engine = new Thread(this); 
            engine.start(); 
        } 
    } 
 
    public void stop() 
    { 
        if(engine != null && engine.isAlive()) 
            engine.stop(); 
        engine = null; 
    } 
 
    public void paint(Graphics gDC) 
    { 
        gDC.drawImage(imageFrames, xPos, 
                      0, null); 
    } 
 
    public boolean mouseUp(Event evt, 
                           int x, int y) 
    { 
        return stopped = true; 
    } 
 
    public void run() 
    { 
        while(!stopped) { 
            xPos = -pos * frameSize; 
            Dimension d = size(); 
            int w = d.width, h = d.height; 
            backBuffer = createImage(w, h); 
            Graphics gDC = backBuffer.getGraphics(); 
            paint(gDC); 
            gDC = getGraphics(); 
            gDC.drawImage(backBuffer, 0, 0, this); 
            try { 
                engine.sleep(100); 
            } 
            catch(InterruptedException e) { 
            } 
            pos = ++pos % frameCount; 
        } 
    } 

 

 
Pozornie  jest  wykreślany  kompletny  zestaw  kadrów,  ale  dzięki  ujemnej  współrzędnej  xPos  i  ograniczeniu 
wykre
ślania do pulpitu apletu, jest każdorazowo wykreślany tylko jeden kadr. 
 
 
_________________________________________________________________________________________ 

Przesyłanie 

 
Aplet pochodzący z sieci rozległej może komunikować się tylko z serwerem z którego został załadowany.  
 
W szczególności, jeśli aplet załadowano na podstawie opisu znajdującego się pod adresem 
 

 

http://www.janb.com/java/cafe/text.html 

 
to może on wykonywać operacje wejścia-wyjścia tylko spod adresu 
 

 

http://www.janb.com/java/cafe/File 

 
w którym File jest nazwą pliku (np. subdir/source.doc

background image

 

168 

 
Uwaga:  Między  adresem  komputera  (tu:  www.janb.com)  a  ścieżką  do  File  może  wystąpić  niejawna  ścieżka 
pośrednicząca wstawiona tam przez serwer. 
 
Przykład Przesyłanie danych 
 

<applet code=Master.class width=100 height=100> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
import java.net.*; 
import java.io.*; 
 
public 
class Master extends Applet { 
    URL url; 
    int EOF = StreamTokenizer.TT_EOF; 
    String text = ""; 
    public void init() 
    { 
        try { 
            url = new URL( 
                      "http://" + 
                      "www.janb.com/java/cafe/" + 
                      "greet.txt" 
                  ); 
        } 
        catch(MalformedURLException e) { 
        } 
        StreamTokenizer tokens = null; 
        try { 
            tokens = new StreamTokenizer(url.openStream()); 
            while(tokens.nextToken() != EOF) 
                text += tokens.sval; 
        } 
        catch(IOException e) { 
        } 
    } 
    public void paint(Graphics gDC) 
    { 
        gDC.drawString(text, 10, 20); 
    } 

 
Wykonanie  apletu  powoduje  wyświetlenie  zawartości  pliku  greet.txt  znajdującego  się  w  katalogu,  z  którego 
pochodzi aplet. 
 
Podczas uruchamiania apletu można plik greet.txt umieścić na dysku lokalnym i zamiast adresu 
 

 

http://www.janb.com/java/cafe/greet.txt 

 
podać na przykład adres 
 

 

file:///c:/java/cafe/greet.txt 

 
 
_________________________________________________________________________________________ 

Komunikacja 

 
 
Komunikowanie się apletów, których opisy znajdują się w tym samym pliku HTML, wymaga nadania im nazw
Nazwy apletów podaje się w opisach apletów we frazie name.  
 

background image

 

169 

Następujący program, pokazany podczas wykonania na ekranie Komunikacja, ilustruje zasadę komunikowania 
się  apletów,  nazwanych  One  i  Two.  Kliknięcie  pulpitu  jednego  z  nich  powoduje  wykreślenie  okręgu  na 
pulpicie drugiego. Opisy apletów w dokumencie HTML są żne, ale B-kod obu apletów jest wspólny
 
Ekran Komunikacja 

### onetwo.gif 

 

<applet code=Master.class width=160 height=100 name=One> 
</applet> 
 
<applet code=Master.class width=160 height=100 name=Two> 
</applet> 
======================================================== 

 

import java.applet.*; 
import java.awt.*; 
 
public 
class Master extends Applet { 
    String name; 
    Thread myThread = null; 
    public void init() 
    { 
        name = getParameter("name"); 
    } 
    Applet otherApplet() 
    { 
        String other = null; 
        if(name.equals("One")) 
            other = "Two"; 
        if(name.equals("Two")) 
            other = "One"; 
        AppletContext context = getAppletContext(); 
        return context.getApplet(other); 
    } 
    public boolean mouseDown(Event evt, int x, int y) 
    { 
        Applet other = otherApplet(); 
        Graphics gDC = other.getGraphics(); 
        gDC.drawOval(x - 20, y - 20, 40, 40); 
        return true; 
    } 
    public boolean mouseUp(Event evt, int x, int y) 
    { 
        otherApplet().repaint(); 
        return true; 
    } 

 
 
_________________________________________________________________________________________ 

Przełączenia 

 
 
Aplet  można  zaprojektować  w  taki  sposób,  aby  wykonanie  operacji  na  jego  sterowniku  powodowało 
przełączenie na inny dokument HTML, niż ten z którego pochodzi właśnie wyświetlany aplet. 
 
Następujący  aplet,  pokazany  podczas  wykonywania  na  ekranie  Wyszukiwarki,  ilustruje  zasadę  przełączania 
stron dokumentów HTML
 
Kliknięcie  jednego  z  przycisków  przywołuje  na  ekran  stronę  domową  jednej  z  ze  znanych  wyszukiwarek: 
AltaVistaHotBot i Yahoo
 
Ekran Wyszukiwarki 

### yahoo.gif 

 

background image

 

170 

<applet code=Master.class width=160 height=100> 
</applet> 
=============================================== 

 

import java.applet.*; 
import java.awt.*; 
import java.net.*; 
 
public 
class Master extends Applet { 
    public void init() 
    { 
        add(new Button("AltaVista"));  // wyszukiwarka 
        add(new Button("HotBot"));     // wyszukiwarka 
        add(new Button("Yahoo"));      // wyszukiwarka 
    } 
    public boolean action(Event evt, Object arg) 
    { 
        URL newURL = null; 
        try { 
            if(!(evt.target instanceof Button)) 
                return false; 
            if(arg.equals("Alta Vista")) 
                newURL = new URL("http://altavista.digital.com/"); 
            else if(arg.equals("Hot Bot")) 
                newURL = new URL("http://hotbot.com/"); 
            else 
                newURL = new URL("http://www.yahoo.com/"); 
        } 
        catch(MalformedURLException e) { 
        } 
        AppletContext context = getAppletContext(); 
        context.showDocument(newURL); 
        return true; 
    } 

 

 

background image

 

171 

Literatura 

 
Bielecki, J.: 

Java po C++, Intersoftland, 1996. 

 

(zwięzła i treściwa, dla programujących w C++) 

 
Chan, P.: 

The Java Class Libraries, Addison Wesley, 1996. 

 

(doskonała, szczegółowy opis bibliotek, w tym AWT

Gosling, J.: 

The Java Language Specification, Addison Wesley, 1996. 

 

(doskonała, obszerny opis języka, bez opisu AWT

Flanagan, D.: 

Java in a Nutshell, O'Reilly & Associates, 1996. 

 

(doskonała, ciekawe przykłady, zwięzłe opisy bibliotek) 

Winston, P.: 

On to Java, Addison Wesley, 1996. 

 

(doskonała, wyszła spod ręki zawodowego dydaktyka) 

 
LeMay, L.: 

Teach Yourself Java in 21 Days, Sams Net, 1996. 

 

(dobra dla początkujących, uproszczona i nieścisła) 

Walrath, K.: 

The Java Tutorial, JavaSoft, 1996. 

 

(dobra dla początkujących, ciekawa i szczegółowa) 

 
Arnold, K.: 

The Java Programming Language, JavaSoft, 1996. 

 

(przeciętna, wiele błędów, mimo współautorstwa J.G.) 

McClellan, A.: 

Java By Example, The SunSoft Press, 1996. 

 

(niekompletna,  łatwa, dobra dla początkujących) 

Linden, P.: 

Just Java, The SunSoft Press, 1996. 

 

(taka sobie, dla niezbyt wymagających) 

Morrison, M.: 

Java Unleashed, Sams Net, 1996. 

 

(przegadana, ale łatwa i szczegółowa) 

Naughton, P.: 

The Java Handbook, Osborne/McGraw-Hill, 1996. 

 

(warta uwagi ze względu na autora, dobra dla początkujących) 

Newman, A.: 

Using Java, QUE, 1996. 

 

(obszerna, taka sobie, kilka ciekawych pomysłów) 

Pew, J.: 

Instant Java, The SunSoft Press, 1996. 

 

(ciekawy zbiór gotowych apletów, nie do nauki języka) 

Rodley, J: 

Writing Java Applets, Coriolis Group Books, 1996. 

 

(przeciętna, głównie o programowaniu sieciowym) 

background image

 

172 

 
 

 
 
 
 
 
 
 
 
 
 
 
 
 

Słownik terminów 

background image

 

173 

A

 

 

abort 

zaniechać 

 

abrubt 

gwałtowny 

 

accessible 

dostępny 

 

active 

aktywny 

 

alpha component 

składnik alfa 

 

ambiguous 

dwuznaczny 

 

applet 

aplet 

 

ascent 

uniesienie 

B

 

 

background 

tło 

 

baseline 

linia bazowa 

 

blinking 

migotanie 

 

boolean value 

orzeczenie 

 

boolean variable 

orzecznik 

 

brightness 

jaskrawość 

 

browser 

przeglądarka 

 

buffering 

buforowanie 

 

button 

przycisk 

 

bytecode 

B-kod 

C

 

 

callback 

obsługa 

 

canvas 

płótno 

 

card 

karta 

 

cast 

konwersja 

 

check mark 

znacznik 

 

checkbox 

nastawa 

 

choice 

wybór 

 

client 

odbiorca 

 

client area 

pulpit 

 

clipping 

obcinanie 

 

clone 

klon 

 

compatible 

zgodny 

 

concurrent 

współbieżny 

 

connection 

połączenie 

 

constraint 

wymuszenie 

 

consumer 

konsument 

 

container 

pojemnik 

 

control 

sterownik 

 

control flow 

przepływ sterowania 

 

control point 

punkt charakterystyczny 

 

copy constructor 

konstruktor kopiujący 

 

critical section 

sekcja krytyczna 

 

cropping 

wycinanie 

 

current 

bieżący 

 

custom 

zamówiony 

D

 

 

daemon 

demon 

 

datagram 

datagram 

 

deadlock 

impas 

 

debugger 

uruchamiacz 

 

default 

domyślny 

 

definitive 

definitywny 

 

delay 

oczekiwanie 

 

deprecate 

zniechęcać 

 

descent 

obniżenie 

 

dialog 

dialog 

 

directory 

katalog 

background image

 

174 

 

domain 

domena 

 

drag 

przeciągać 

 

drop down 

opadać 

 

dump 

zrzut 

 

elaboration 

opracowanie 

 

ellipsis 

wielokropek 

 

embedded 

wbudowany 

 

envelope 

koperta, otoczka 

 

environment 

otoczenie 

 

event driven 

sterowany zdarzeniami 

 

event 

zdarzenie 

F

 

 

factory expression 

wyrażenie fabrykujące 

 

factory method 

metoda fabrykująca 

 

file 

plik 

 

filtered 

filtrowany 

 

final 

ustalony 

 

firewall 

zapora 

 

flow of control 

przepływ sterowania 

 

flush 

wymieść 

 

folder 

katalog 

 

foreground 

lico 

 

frame 

ramka 

 

friendship 

zaprzyjaźnienie 

G

 

 

garbage 

nieużytek 

 

graphics context 

kontekst graficzny 

 

handler 

obsługa 

 

heap 

sterta 

 

height 

wysokość 

 

home page 

strona domowa 

 

host 

gospodarz 

 

hue 

odcień 

 

identifier 

identyfikator 

 

incremental 

przyrostowy 

 

initializer 

inicjator 

 

instance 

egzemplarz 

 

integrity 

integralność 

 

interface 

oblicze 

 

l-value 

l-wyrażenie 

 

label 

etykieta 

 

layout 

rozkład 

 

leading 

ś

wiatło 

 

linking 

łączenie 

 

list 

lista 

 

loading 

ładowanie 

 

locator 

lokalizator 

M

 

 

manager 

zarządca 

 

member 

składnik 

 

metrics 

metryka 

 

modal 

dominujący 

background image

 

175 

 

multiple inheritance 

wielodziedziczenie 

 

multithreaded 

wielowątkowy 

N

 

 

native 

rodzimy 

 

narrowing 

zawężenie 

 

null 

pusty 

O

 

 

observer 

obserwator 

 

opaque 

nieprzeźroczysty 

 

overloaded 

przeciążony 

 

override 

przedefiniować 

P

 

 

 

package 

pakiet 

 

passive 

bierny 

 

 

 

peer 

równorzędny 

 

pipe 

potok 

 

pointer 

pozycja 

 

port 

port 

 

portable 

przenośny 

 

preempt 

wywłaszczyć 

 

preferred 

ulubiony 

 

prepared 

przygotowany 

 

priority 

priorytet 

 

process 

proces  

 

producer 

producent 

 

property 

właściwość 

 

protocol 

protokół 

R

 

 

radio button 

przełącznik 

 

rank 

ranga 

 

reanimate 

wskrzesić 

 

recursive 

rekurencyjny 

 

reentrant 

wielobieżny 

 

reference 

odnośnik 

 

region 

obszar 

 

relative 

względny 

 

rendering 

wizualizacja 

 

repetetive 

cykliczny 

 

resolve 

związać 

 

rest 

spoczywać 

 

reusable 

wieloużytkowy 

 

rounding 

zaokrąglenie 

 

run 

wykonywać 

 

saturation 

nasycenie 

 

scope 

zakres 

 

security 

bezpieczeństwo 

 

self contained 

samodzielny 

 

server 

dostawca 

 

session 

sesja 

 

shadow 

przesłonić 

 

shallow 

płytki 

 

shared 

dzielony 

 

side effect 

skutek uboczny 

 

signature 

sygnatura 

 

size 

rozmiar 

 

sleep 

spać 

background image

 

176 

 

socket 

gniazdo 

 

source 

ź

ródło 

 

starved 

zagłodzony 

 

stream 

strumień 

 

stub 

pień 

 

style 

styl 

 

subclass 

podklasa 

 

superclass 

nadklasa 

 

supported 

utrzymywany 

 

suspended 

zawieszony 

 

target 

cel 

 

task 

zadanie 

 

teller 

kasjer 

 

text area 

notatnik 

 

text field 

klatka 

 

thread 

wątek 

 

tracker 

nadzorca 

 

transient 

nietrwały 

 

transparent 

przeźroczysty 

 

typeface 

krój 

U

 

 

unbalanced 

niezrównoważony 

V

 

 

verify 

weryfikować 

 

virtual 

pozorny 

 

volatile 

ulotny 

 

wait 

czekać 

 

widening 

poszerzenie 

 

widget 

sterownik 

background image

 

177 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Dodatki 

 

background image

 

178 

Dodatek A 

Priorytety operatorów 

 
Następujące zestawienie wyszczególnia pełen zestaw operatorów, wraz z ich priorytetami i wiązaniami
 
 

Priorytet 

Wiązanie 

Operator 

 

 

 

 

lewe 

++  --  (następnikowe

 

prawe 

++  -- (poprzednikowe

 

prawe 

+  -  ~  !  (Type) 

 

lewe 

*  /  % 

 

lewe 

+  - 

 

lewe 

<<  >>  >>> 

 

lewe 

<  <=  >  >=  instanceof 

 

lewe 

==  != 

 

lewe 

& 

 

10 

lewe 

^ 

 

11 

lewe 

| 

 

12 

lewe 

&& 

 

13 

lewe 

||     

 

14 

prawe 

?: 

 

15 

prawe 

=  *=  /=  %=  +=  -=  <<=  >>=  >>>= &=  ^=  |= 

 

background image

 

179 

Dodatek B 

Parametry zdarzeń 

 
Następujące zestawienie określa w jaki sposób komponenty reagują na zdarzenia.  W nawiasach podano jakie 
spośród  pól  arg,  when,  x,  y,  modifiers  mają  interpretację  dla  wymienionych  komponentów.  Pola  target  nie 
wymieniono, bo ma interpretację zawsze.  
 

ACTION_EVENT 

wykonanie akcji na sterowniku 

    Button 

przycisk 

(arg: etykieta 

String)         

    Checkbox 

nastawa 

(arg: nowy stan 

boolean) 

    Choice 

wybór 

(arg: etykieta 

String) 

    List 

lista 

(arg: etykieta 

String) 

    MenuItem 

polecenie 

(arg: etykieta 

String) 

    TextField 

klatka 

(arg: zawarto

ść

 

String) 

 
 
MOUSE_DOWN 

naci

ś

ni

ę

cie przycisku myszki 

MOUSE_UP 

zwolnienie przycisku myszki 

MOUSE_MOVE 

przemieszczenie kursora 

MOUSE_DRAG 

przeci

ą

gni

ę

cie kursora 

    Component 

 

(whenxymodifiers

 
 
MOUSE_ENTER 

wej

ś

cie kursora w obszar komponentu 

MOUSE_EXIT 

wyj

ś

cie kursora z obszaru komponentu 

    Component 

 

(whenxy

 
 
LIST_SELECT 

wyró

ż

nienie elementu listy 

LIST_DESELECT 

usuni

ę

cie wyró

ż

nienia 

    List 

 

(arg: indeks 

int) 

 
 
GOT_FOCUS 

uzyskanie gotowo

ś

ci do odbierania znaków 

LOST_FOCUS 

utrata gotowo

ś

ci 

    Component 
 
 
KEY_PRESS 

naci

ś

ni

ę

cie zwykłego klawisza 

KEY_RELAEASE 

zwolnienie zwykłego klawisza 

KEY_ACTION 

naci

ś

ni

ę

cie klawisza funkcjyjnego 

KEY_ACTION_RELEASE  zwolnienie klawisza funkcyjnego 

    Component 

 

(whenxykeymodifiers

 
 
SCROLL_LINE_UP 

małe przewini

ę

cie do przodu (wiersz, znak) 

SCROLL_LINE_DOWN 

małe przewini

ę

cie wstecz 

SCROLL_PAGE_UP 

du

ż

e przewini

ę

cie do przodu (strona) 

SCROLL_PAGE_DOWN 

du

ż

e przewini

ę

cie wstecz 

SCROLL_ABSOLUTE 

przewini

ę

cie na pozycj

ę

 

    Scrollbar 

 

(arg: pozycja 

int) 

 
 
WINDOW_DESTROY 

zniszczenie okna 

WINDOW_EXPOSE 

odsłoni

ę

cie okna 

WINDOW_ICONIFY 

zmniejszenie okna do ikony 

WINDOW_DEICONIFY 

powi

ę

kszenie ikony do okna 

WINDOW_MOVED 

przemieszczenie okna 

    Window 

 

(xy

 

background image

 

180 

Dodatek C 

Klasa uruchomieniowa 

 
 

Zdefiniowana  tu  klasa  uruchomieniowa  Debug  ułatwia  wyszukiwanie  błędów  występujących  podczas 
wykonywania aplikacji i apletów. 
 
Wykonanie instrukcji 
 

new Debug(); 

 
powoduje  utworzenie  ramki,  w  której  są  wyświetlane  argumenty  przeciążonej  funkcji  toFrame  oraz  funkcji 
assert
 
Uwaga: Przed użyciem klasy Debug w wersji źródłowej należy upewnić się, że moduł zawiera polecenie 
 

 

import java.awt.*; 

 
Na przykład 
 

Debug.toFrame("Hello"); 
Debug.toFrame(var+1); 
Debug.toFrame("var = " + var); 
Debug.toFrame(Thread.currentThread()); 
 
Debug.assert("In paint: boxWidth > 5", boxWidth > 5); 
 

Bezpośrednio  po  wywołaniu  funkcji  off,  a  przed  najbliższym  wywołaniem  funkcji  on,  wstrzymuje  się 
wy
świetlanie argumentów metod toFrame assert
 
Uwaga: Jeśli nie wywołano konstruktora klasy Debug, to wywoływanie jej funkcji jest ignorowane
 
Na przykład 
 

new Debug(100, 100);  // wywołanie konstruktora 
// ... 
Debug.off();          // zaniechanie wy

ś

wietlania 

// ... 
Debug.on();           // przywrócenie wy

ś

wietlania 

// ... 

 
Uwaga:  Wykonanie  operacji  new  Debug()  może  być  tylko  jednokrotne.  W  przypadku  naruszenia  tego 
wymagania jest wysyłany wyjątek klasy InstantiationError

 
 
import java.awt.*;    // wymagane zawsze 
 
class Debug { 
 
/**  
    Klasa uruchomieniowa 
    Copyright © Jan Bielecki 
 */ 
 
    static Frame frame; 
    String header = "Debug\n=====\n"; 
    static TextArea textArea; 
    static boolean opened = false; 
    static boolean active = false; 
    Debug() 
    { 
        this(200, 400); 
    } 
    Debug(int width, int height) 
    { 
        if(opened) 
            throw new InstantiationError(); 

background image

 

181 

        opened = true; 
        active = true; 
        frame = new DebugFrame("Debug"); 
        frame.resize(width, height); 
        textArea = new TextArea(header); 
        frame.add("Center", textArea); 
        frame.show(); 
    } 
    static synchronized  
    void toFrame(String string) 
    { 
        if(active) 
            textArea.appendText(string + '\n'); 
    } 
    static synchronized 
    void toFrame(double num) 
    { 
        toFrame("" + num); 
    } 
    static synchronized  
    void toFrame(Object object) 
    { 
        if(active) 
            toFrame(object.toString()); 
    } 
    static synchronized  
    void assert(String what, boolean isTrue) 
    { 
        if(active && !isTrue) 
            textArea.appendText("Assertion \"" + what +  
                                "\" failed\n"); 
    } 
    static synchronized void on() 
    { 
        if(opened) 
            active = true; 
    } 
    static synchronized void off() 
    { 
        active = false; 
    } 

 
class DebugFrame extends Frame { 
    DebugFrame(String caption) 
    { 
        super(caption); 
    } 
    public boolean handleEvent(Event evt) 
    { 
        if(evt.id == Event.WINDOW_DESTROY) { 
            hide(); 
            dispose(); 
            return true; 
        } 
        return super.handleEvent(evt); 
    } 

background image

 

182 

Dodatek D 

Styl programowania 

 
Istnieje tyle stylów programowana ilu jest programistów. Jednak bez trudu można stwierdzić, że jedne style są 
lepsze od innych. Dlatego, w celu ujednolicenia zapisu i tym samym ułatwienia wymiany programów, podano 
tu opis stylu zastosowanego w niniejszej książce. 
 
 

Wyglą

Program zapisuje się czcionką równomierną, na przykład Courier. Żaden wiersz programu nie liczy więcej niż 
60 znaków.  
 
W  celu  spełnienia  tych  wymagań  oraz  zwiększenia  czytelności  kodu  stosuje  się  łamanie  wierszy  i  dodawanie 
odstępów  (spacji  i  pustych  wierszy).  Łamania  wyrażeń  dokonuje  się  w  taki  sposób,  aby  ostatnim  znakiem 
złamanego wiersza był operator
 

public static  
boolean draw(Graphics gDC,  
             Color color,  
             int x,     int y,  
             int width, int height) 

    StreamTokenizer tokens = new StreamTokenizer( 
                                 "c:\\cafe" + 
                                 "myfile.txt" 
                             ); 
    // ... 

 
 

Konsekwencja 

Identyczne konstrukcje języka zawsze wyglądają tak samo
 

int arr[]; 
int[] vec;                  // niekonsekwentnie 

 

System.out.println(); 
System.out.print('\n');     // niekonsekwentnie 

 
 

Nadmiarowość 

Nadmiarowość  jest  dozwolona  tylko  w  celu  zwiększenia  czytelności  programu.  Dotyczy  to  w  szczególności: 
użycia zbędnego odnośnika this, użycia zbędnych nawiasów oraz użycia zbędnych specyfikatorów. 
 

class Square { 
    int side; 
    Square(int base) 
    { 
        this.side = base;           // zb

ę

dne this 

        // ... 
        if((a > 0) && (a < 10))     // zb

ę

dne nawiasy 

            System.exit(0); 
    } 
    // ... 

 
abstract                            // zb

ę

dne abstract 

interface Discardable  

    static final int ZERO = 0;      // zb

ę

dne static i final 

    // ... 

 
     

Komentarze 

background image

 

183 

Komentarz wierszowy jest wyrównany w pionie względem pozostałych takich komentarzy z jego otoczenia. 
 

int x;   // xCen 
int y;   // yCen 

 
Komentarz blokowy zapisuje się od pierwszej kolumny. W wierszach następnych zapisuje się dodatkowy znak 
* (gwiazdka). Zakończenie komentarza zapisuje się w osobnym wierszu. Ogół gwiazdek tworzy linię pionową. 
 

/* Hello 
 * from 
 * me  
 */ 

 
Uwaga:  W  dalszym  opisie  istnienie  komentarzy  nie  będzie  brane  pod  uwagę.  W  tym  sensie  za  ostatni  znak 
wiersza 
 

    int x; // xCen 

 
będzie odtąd uznawany średnik. 
 
 

Wcięcia 

Jeśli  pewna  konstrukcja  składniowa  zawiera  inną  (na  przykład  definicja  klasy  zawiera  deklarację  metody),  to 
konstrukcję wewnętrzną wcina się względem zewnętrznej. 
 
Wcięcie uzyskuje się przez wstawienie ciągu spacji. Minimalny rozmiar wcięcia: 4 spacje. Wszystkie wcięcia 
muszą być równe. Znaku tabulacji nie używa się. 
 

class Greet { 
    int fun(int a) 
    { 
        if(a < 0) 
            return 0; 
        return a * a; 
    } 

 
 

Identyfikatory 

Identyfikatory  pakietów  zaczynają  się  od  małej  litery.  Identyfikatory  typów,  stałych  i  etykiet  od  dużej
Identyfikatory pólzmiennych i procedur od małej.  
 
Zaleca się dobieranie nazw mnemonicznych, na przykład 
 
 

getV i setV 

dla pobrania/zmiany (np. getColorsetColor

 

isV 

dla badania (np. isEmpty

 

toV 

dla konwersji (np. toString

 
Nazwy jednoliterowe stosuje się tylko w specjalnych przypadkach, np. 
 
 

b 

dla byte 

 

c 

dla char 

 

d 

dla double 

 

e 

dla Exception 

 

f 

dla float 

 

ijk 

dla liczników 

 

l 

dla long 

 

o 

dla Object 

 

s 

dla String 

 

v 

dla wartości 

 

background image

 

184 

W  identyfikatorach  wielosłowowych  stosuje  się  duże  litery,  a  nie  znaki  podkreślenia.  Znaku  podkreślenia 
używa się tylko w identyfikatorach stałych. 
 

int index; 
void getArea(); 
public anyVeryLongMeaningfulName; 
public static final double Pi = 3.14; 
final Color COLOR_RED = Color.red; 

 
 

Separatory 

Po  każdym  przecinku,  dwukropku  i  średniku  występuje  odstęp  (wyjątek:  instrukcja  for).  Przed  przecinkiem 
oraz średnikiem oraz po nawiasie otwierającym i przed nawiasem zamykającym nie stawia się odstępu (wyjątek: 
ś

rednik w instrukcji for). 

 

fun(a, fun(a, b), b); 
fun ( a , b );            // zb

ę

dne odstepy 

fun(a, b);                // dobrze 

 
 

Specyfikatory 

Specyfikatory  klas  i  interfejsów  wymienia  się  w  następującej  kolejności:  dostępność  (public,  protected
private), abstrakcyjność (abstract), ustaloność (final),  rodzaj (classinterface). 
 
Specyfikatory  procedur  wymienia  się  w  następującej  kolejności:  dostępność  (public,  protected,  private), 
statyczność  (static),  abstrakcyjność  (abstract),  rodzimość  (native),    ustaloność  (final),  synchroniczność 
(synchronized), typ (voidint, itp.). 
 
Specyfikatory  pól  i    zmiennych  wymienia  się  w  następującej  kolejności:  dostępność  (public,  protected
private),  statyczność  (static),  nietrwałość  (transient),  ulotność  (volatile),  ustaloność  (final),  typ  (void,  int
itp.). 
 

public abstract  
class Input { 
    protected static void fun(); 
    public abstract synchronized int met(int x); 
    private static volatile final int var = 12; 
    // ... 

 
 

Bloki 

Klamrę  otwierającą  ciało  klasy  albo  interfejsu  oraz  blok  zawarty  w  innej  instrukcji  (np.  w  inicjatorze  klasy) 
umieszcza  się  po  spacji,  w  tym  samym  wierszu  co  nagłówek  klasy  albo  interfesju,  albo  początkowa  fraza 
instrukcji.  W  pozostałych  przypadkach  klamrę  otwierającą  umieszcza  się  w  osobnym  wierszu  (dotyczy  to 
przede wszystkim klamry otwierającej ciało funkcji).  
 
Klamrę zamykającą blok  zawsze umieszcza się w osobnym wierszu. 
 

class Any { 
    void fun() 
    { 
        while(true) { 
            int i = 0; 
            { 
                // .. 
            } 
        } 
    } 

 
 

Definicje klas i interfejsów 

background image

 

185 

Słowo kluczowe class i interface umieszcza się od pierwszej kolumny wiersza. Jeśli definicja jest poprzedzona 
specyfikatorami, to umieszcza się je od pierwszej kolumny poprzedniego wiersza. 
 

public abstract 
class Greet { 
    // ... 

 
 

Definicje procedur 

Nagłówek  procedury  umieszcza  się  na  początku  wiersza,  z  jednym  wcięciem.  Klamrę  otwierającą  ciało 
procedury  umieszcza  się  w  osobnym  wierszu.  W  przypadku  licznych  parametrów dopuszcza  się  przeniesienia 
po przecinku, wcięte do pierwszego parametru. 
 

public 
class Point { 
    Point(int x) 
    { 
        // ... 
    } 
    Point(int a, int b, int c, 
          int d, int e) 
    { 
        // ... 
    } 

 
 

Etykiety 

Etykieta zaczyna się w osobnym wierszu, począwszy od tej samej kolumny, od której zaczyna się następująca 
po niej instrukcja. 
 

Again: 
while(a > 5) { 
    // ... 
    break Again; 

 
 

Wyrażenia 

Między  pustymi  nawiasami  kwadratowymi  i  okrągłymi,  po  obu  stronach  kropki  oraz  po  operatorze 
jednoargumentowym  nie  umieszcza  się  spacji.  Po  obu  stronach  operatora  dwuargumentowego  umieszcza  się 
spację. 
 

int arr[]; 
int len = +arr.length() + 1; 

 
Uwaga: Jeśli argumentem operacji dwuargumentowej jest liczba, to zezwala się, aby operator nie był otoczony 
spacjami jeśli zwiększy to czytelność programu. 
 

arr[2*i+1] = 2*(i-1); 

 
Wyrażenie  które  nie  mieści  się  w  wierszu  jest  dzielone  między  kolejne  wiersze  w  taki  sposób,  że  ostatnim 
znakiem wiersza jest operator. Przeniesiona do następnego wiersza część wyrażenia jest wówczas wyrównana 
albo wcięta i wyrównana
 

System.out.println("Hello" + 
                   " " + 
                   "World"); 
System.out.println( 
               "Hello" + 
               " " + 
               "World" 
           ); 

background image

 

186 

 
 

Instrukcje 

Każda instrukcja i podinstrukcja zaczyna się w osobnym wierszu. 
 

if(a > 5) break;    // 

ź

le 

if(a > 5) 
    break; 

 

instrukcje if 

Jeśli po frazie if występuje klamra otwierająca, to odpowiadającą jej klamrę zamykającą umieszcza się pod if
 
Jeśli po frazie else występuje fraza if, to frazy else i if zapisuje się w tym samym wierszu. 
 
Jeśli instrukcja if zawiera frazę frazą else, to każdy z następujących napisów 
 

 

else 

 

} else 

 

} else { 

 
umieszcza się w osobnym wierszu,  w pionowym wyrównaniu pod if
 

if(a > 5) 
    fun(a, b) 
 
if(a > 5) 
    fun(a, b) 
else { 
    fun(c, d); 
    fun(e, f); 

 
if(a > 5) { 
    fun(a, b); 
    fun(c, d); 
} else { 
    fun(e, f); 
    return; 

 
if(a > 5) 
    fun(i); 
else if(b > 5) { 
    fun(i+1); 
    return; 

 

instrukcje for 

Przed pierwszym średnikiem frazy zawartej w nagłówku instrukcji for nie stawia się spacji. Po każdej stronie 
drugiego umieszcza się jedną spację.  
 

for(int i = 0; i < 5 ; i++) { 
    fun(i); 
    fun(i+1); 

 

instrukcje while 

Jeśli ciałem instrukcji while jest blok, to zamykająca go klamra występuje w pionowym wyrównaniu pod while
 

while(a > 5) { 
    fun(i); 
    fun(i+1); 

 

instrukcje do 

background image

 

187 

Jeśli  ciałem  instrukcji  do  jest  blok,  to  otwierająca  go  klamra  występuje  w  tym  samym  wierszu  co  nawias 
zamykający nagłówek, a napis  
 

 

} while(  

 
występuje w pionowym wyrównaniu pod do
 

do { 
    fun(i); 
    fun(i+1); 
} while(i < 4); 

 
 

instrukcje switch 

Klamra otwierająca blok instrukcji switch występuje w tym samym wierszu co nagłówek instrukcji. 
 
Każda fraza case zaczyna się i kończy w osobnym wierszu. Instrukcje frazy case są względem niej wcięte
 

switch(a) { 
    case 0:  
        a = 3; 
        break; 
    case 1:  
        a = 4; 

 

instrukcje try 

Klamra  otwierająca  bloku  try  występuje  w  tym  samym  wierszu  co  słowo  kluczowe  try.  Każda  fraza  catch 
zaczyna się od nowego wiersza. 
 

try { 
    fun(i); 

catch(IOException e) { 

catch(Exception e) { 
    System.out(e.getMessage()); 

finally { 
    i = 0; 

 

background image

 

188 

Dodatek E 

Typy predefiniowane 

 
Predefiniowanymi typami danych są typy orzecznikowearytmetyczne i łańcuchowe. Typy arytmetyczne dzielą 
się na całkowite (byteshortintlong), rzeczywiste (floatdouble) i znakowe (char).  Typami łańcuchowymi 
są predefiniowane typy obiektowe: String i StringBuffer
 
Uwaga:  Ponieważ  typ  znakowy  jest  typem  arytmetycznym,  więc  na  jego  danych  można  wykonywać  takie 
same operacje jak na danych całkowitych rzeczywistych
 
 

Typ orzecznikowy 

 
Typem  orzecznikowym  jest  typ  "boolean".  Zmienne  typu  orzecznikowego  mogą  przybierać  wartości  true 
(prawda) i false (fałsz). 
 
Każde wyrażenie orzecznikowe exp może być poddane konwersji na wyrażenie całkowite za pomocą operacji 
 
 

exp ? 1 : 0 

 
(por.  Dodatek  Operacje  i  operatory),  a  każde  wyrażenie  całkowite  exp  może  być  poddane  konwersji  na 
wyrażenie orzecznikowe za pomocą operacji 
 
 

exp != 0 

 
 

Typy całkowite 

 
Typami całkowitymi są: "byte", "short", "int" i "long". Zmienne typów całkowitych są 
 
 

byte 

8-bitowe 

(od  -128 do 127) 

 

short 

16-bitowe 

(od  -32768 do 32767) 

 

int 

32-bitowe 

(od  -2,147,483,648 do 2,147,483,647) 

 

long 

64-bitowe 

(od  -9,223,372,036,854,775,808 do 

 

+9,223,372,036,854,775,807) 

 

 

Dane  przypisane  zmiennym  całkowitym  są  reprezentowane  w  uzupełnień  do  2.  Wynika  stąd  m.in.,  że  jeśli 
zaneguje się wszystkie bity danej, a następnie doda do niej 1, to skutek będzie taki, jakby znak danej zmieniono 
na przeciwny
 
Typ  liczby  wynika  jednoznacznie  z  jej  wartości  (jako  kryterium  wyboru  przyjmuje  się  oszczędność 
reprezentacji).  
 
Niezależnie od jej wartości, liczba zakończona literą L albo l jest typu "long".  
 
Na przykład 32767 jest typu "short", a 32768 jest typu "int",  ale 0L jest typu "long". 
 
 

Typy rzeczywiste 

 
Typami rzeczywistymi są: "float" i "double". Zmienne typów całkowitych są:  
 
 

float 

32-bitowe (w przybliżeniu od -3.4e38 do 3.4e38) 

 

double 

64-bitowe (w przybliżeniu od -1.8e308 do 1.8e308) 

 
Dane przypisane zmiennym rzeczywistym są reprezentowane w zapisie modułu-i-znaku, a wyniki nietypowych 
operacji, jak na przykład 

background image

 

189 

 
 

1.0 / 0 

(Double.POSITIVE_INFINITY

 

1.0 /-0 

(Double.NEGATIVE_INFINITY

 

1.0 / 0 * 0 

(Double.NaN tj. NotANumber

 
mają wartości, które można reprezentować przez symbole podane w nawiasach. 
 
Każda liczba z wąskiego przedziału wokół 0 jest reprezentowana przez zero-ze-znakiem (tj. ujemne-zero albo 
dodatnie-zero). Z punktu widzenia relacji równe i  nie-równe oba te zera uznaje się za równe.  
 
Typ  liczby  rzeczywistej  wynika  jednoznacznie  z  jej  wartości  (jako  kryterium  wyboru  przyjmuje  się 
oszczędność reprezentacji).  
 
Każda prosta liczba rzeczywista (np. -1.21.-.21.2e-1) oraz każda prosta liczba rzeczywista zakończona literą 
D albo d jest typu "double". Każda prosta liczba całkowita albo rzeczywista zakończona literą F albo f jest typu 
"float".  
 
Na przykład -2.4 oraz 3e2 jest typu "double", a -2e-3f jest typu "float". 
 
 

Typ znakowy 

 
Typem  znakowym  jest  "char".  Zmiennym  typu  znakowego  są  zazwyczaj  przypisywane  16-bitowe  znaki 
Unikodu.  Unikod  umożliwia  reprezentowanie  znaków  wszystkich  języków  europejskich  oraz  większości 
znaków pozostałych języków, w tym ideograficznych znaków Han używanych w krajach azjatyckich.  
 
Pierwszych 256 Unikodu jest identycznych ze znakami kodu ASCII Latin-1 (kod ten zawiera m.in. większość 
znaków języków zachodnio-europejskich). 
 
Literały  typu  "char"  mają  postać  'c'  gdzie  c  jest:  znakiem  widocznym  (np.  'a'),  symbolem  znaku  (np.  '\n'), 
ósemkowym kodem znaku (np. '\141') albo czterocyfrowym szesnastkowym kodem znaku (np. '\u0061'). 
 
 

Typy łańcuchowe 

 
 
Predefiniowanymi  typami  łańcuchowymi  są  String  i  StringBuffer.  W  istocie  nie  są  to  odrębne  typy,  gdyż 
zostały  implementowane  jako  klasy  obiektowe.  Jednak  ze  względu  na  szczególną  rolę  jaką  pełnią  w  języku, 
zasługują na specjalne potraktowanie. 
 
Zarówno  String,  jak  i  StringBuffer,  są  klasami  ustalonymi  (final),  to  jest  takimi,  które  nie  mogą  być 
dziedziczone. Właściwość ta zapewnia im bardzo efektywną implementację. 
 
Obiekty klasy String są niemodyfikowalne, a więc, jeśli rezultat pewnej operacji jest klasy String, to nie musi 
być  odrębnym  obiektem.  Obiekty  klasy  StringBuffer  są  modyfikowalne.  Klasa  StringBuffer  jest  używana 
przede wszystkim do tworzenia nowych obiektów klasy String.  
 
W szczególności, wyrażenie  
 

4 + "sale" 

 
jest niejawnie przekształcane w wyrażenie 
 

new StringBuffer().append(4).append("sale").toString() 

 
 

Klasa String 

 

background image

 

190 

Metody  klasy  String  są  używane  do  porównywania,  porządkowania,  rozpoznawania,  wycinania  i 
przekształcania znaków.  
 
Uwaga: Znaki łańcuchów są indeksowane od 0. Jeśli metoda ma zwrócić indeks znaku, ale takiego znaku nie 
ma, to zwraca wartość -1
 
 

boolean equals(Object obj) 

Metoda  służy  do  porównania  łańcucha  z  obiektem  przekształconym  w  łańcuch  (najczęściej  z  innym 
łańcuchem). 
 

String hello = "Hello"; 
String world = "World"; 
boolean result = hello.equals(world);   // false 

 
 

int compareTo(String string) 

Metoda  służy  do  porównania  dwóch  łańcuchów.  Jeśli  są  równe,  to  rezultatem  jest  0.  Jeśli  pierwszy  jest 
mniejszy, to rezultatem jest liczba ujemna, a jeśli drugi, to dodatnia
 

String hello = "Hello"; 
String world = "World"; 
int value = hello.compareTo(world); 
if(value < 0) 
    text = "less"; 
else if(value == 0) 
    text = "equal"; 
else 
    text = "greater"; 
boolean result = text.equals("less");    // true 

 
 

char charAt(int pos) 

Metoda zwraca znak występujący na podanej pozycji łańcucha. 
 

String string = "Hello"; 
char chr = string.charAt(4);                // 'o' 

 
 

int indexOf(char chr) 

Metoda zwraca indeks pierwszego wystąpienia podanego znaku w łańcuchu. 
 

String string = "buffer"; 
int result = string.indexOf('u');           //1 

 
 

int indexOf(String string) 

Metoda zwraca indeks pierwszego znaku stanowiącego podany podciąg łańcucha. 
 

String string = "remaining"; 
int pos = string.indexOf("main");           // 2 

 
 

String substring(int from, int to) 

Metoda  zwraca  podciąg  łańcucha  składający  się  ze  znaków  występujących  "między"  znakami  o  podanych 
indeksach. 
 

String string = "0123456789"; 
String result = string.substring(2, 6);  // 2345 (sic!) 

 
 

String toUpperCase(String string) 

Metoda zwraca łańcuch powstały z oryginału przez zamianę każdej małej litery na dużą. 
 

background image

 

191 

String string = "Hello"; 
String result = string.toUpperCase();  // HELLO 

 
 

String toLowerCase(String string) 

Metoda zwraca łańcuch powstały z oryginału przez zamianę każdej dużej litery na małą. 
 

String string = "Hello World"; 
String result = string.toLowerCase();  // hello world 

 
 

Klasa StringBuffer 

 
Metody  klasy  StringBuffer  są  używane  do  przekształcania  ciągów  znaków.  W  odróżnieniu  od  metod  klasy 
String, metody te na ogół nie tworzą nowych obiektów, ale tylko modyfikują je. 
 
 

void setCharAt(int index, char chr) 

Metoda zmienia znak na podanej pozycji łańcucha. 
 

StringBuffer string = new StringBuffer("Hello World"); 
string.setCharAt(5, '*'); 
String result = new String(string);    // Hello*World 

 
 

StringBuffer append(char chr) 

Metoda wydłuża łańcuch o podany znak. 
 

StringBuffer string = new StringBuffer("Hello"); 
string.append('*').append("World"); 
String result = new String(string);    // Hello*World 

 
 

StringBuffer append(String string) 

Metoda wydłuża łańcuch o podany łańcuch. 
 

StringBuffer string = new StringBuffer("Hello"); 
string.append("World"); 
String result = new String(string);    // HelloWorld 

 
 

StringBuffer append(char arr[], int offset, int len) 

Metoda wydłuża łańcuch o znaki podanego wycinka tablicy. 
 

char arr[] = { ' ', 'W', 'o', 'r', 'l', 'd' }; 
StringBuffer string = new StringBuffer("Hello"); 
string.append(arr, 1, 5); 
String result = new String(string);    // HelloWorld 

 
 

StringBuffer append(Object obj) 

Metoda  wydłuża  łańcuch  o  łańcuch  utworzony  z  podanego  obiektu,  po  wywołaniu  na  jego  rzecz  metody 
toString jego klasy. 
 

Double value = new Double(2 - 3.4); 
StringBuffer string = new StringBuffer("x"); 
string.append(value); 
String result = new String(string);    // x-1.4 

 
 

StringBuffer insert(int offset, char chr) 

Metoda wstawia podany znak w podanym miejscu łańcucha. 
 

StringBuffer string = new StringBuffer("HelloWorld"); 

background image

 

192 

string.insert(5, '*'); 
String result = new String(string);    // Hello*World" 

 
 

StringBuffer insert(int offset, String string) 

Metoda wstawia podany łańcuch w podanym miejscu łańcucha. 
 

StringBuffer string = new StringBuffer("HelloWorld"); 
string.insert(5, "***"); 
String result = new String(string);    // Hello***World" 

 
 

StringBuffer insert(int offset, char arr[]) 

Metoda wstawia znaki tablicy w podanym miejscu łańcucha. 
 

char arr[] = { '*', ' ', '*' }; 
StringBuffer string = new StringBuffer("HelloWorld"); 
string.insert(5, arr); 
String result = new String(string);    // Hello* *World" 

 
 

StringBuffer insert(int offset, Object obj) 

Metoda  wstawia  znaki  łańcucha  utworzonego  z  podanego  obiektu,  po  wywołaniu  na  jego  rzecz  metody 
toString jego klasy. 
 

Integer value = new Integer(2); 
StringBuffer string = new StringBuffer("x"); 
string.insert(0, value); 
String result = new String(string);    // 2x 

 
 

Typy kopertowe 

 
Typami  kopertowymi  są  predefiniowane  typy  obiektowe  "Boolean",  "Integer",  "Long",  "Float",  "Double", 
"Character. Obiekt typu kopertowego stanowi otoczkę dla zmiennej odpowiadającego mu typu podstawowego: 
"boolean", "int", "long", "float", "double", "char". 
 
Na  zmiennych  typu  kopertowego  można  wykonywać  wiele  operacji,  których  nie  można  wykonywać  na 
zmiennych typu podstawowego. 
 
W  szczególności,  zmienne  typu  kopertowego można tworzyć  na stercie, a za pomocą funkcji ich typu  można 
dokonywać wielu użytecznych konwersji, podanych tu dla typu "Integer". 
 
 

public static Integer valueOf(String str) 

Rezultatem  funkcji  jest  zmienna  typu  kopertowego  zainicjowana  wartością  liczby  zawartej  w  podanym 
łańcuchu. 
 

Integer var(0); 
var = Integer.valueOf("20"); 
System.out.print(var);           // 20 
 
 
public static String toString(int val) 

Rezultatem funkcji jest odnośnik do obiektu zainicjowanego łańcuchem utworzonym z argumentu. 
 

int var = 20; 
String str = var.toString(); 
System.out.print(str);           // 20 

 

public static int parseInt(String str) 

Rezultatem  funkcji  jest  zmienna  typu  podstawowego  zainicjowana  wartością  liczby  zawartej  w  podanym 
łańcuchu. 

background image

 

193 

 

int val = Integer.parseInt(str); 

 

background image

 

194 

Dodatek F 

Operacje i operatory 

 
Opisem  operacji  jakie  maj¹  byæ  wykonane  na  danych  jest  wyra¿enie.  O  kolejnoœci  wykonania  operacji 
wchodz¹cych  w  sk³ad  wyra¿enia  decyduje  sposób  u¿ycia  nawiasów  oraz  uwzglêdnienie  priorytetów  i  wi¹zañ 
operatorów  (por.  Dodatek  Priorytety  operatorów).  We  wszystkich  pozosta³ych  przypadkach  wyra¿enia  s¹ 
opracowywane  œciœle  od-lewej-do-prawej  (dotyczy  to  w  szczególnoœci  kolejnoœci  opracowywania 
argumentów procedur). 
 
 

Wyrażenia stałe 

 
W niektórych miejscach programu (na przyk³ad w wyra¿eniach fraz case), mo¿e wyst¹piæ tylko wyra¿enie sta³e 
ca³kowite
.  
 
W sk³ad takiego wyra¿enia mog¹ wchodziæ jedynie litera³y,  identyfikatory statycznych zmiennych ustalonych 
zainicjowanych wyra¿eniami sta³ymi oraz konwersje do typu ca³kowitego. 
 
Na przyk³ad 
 

class Any { 
    static final int one = 1; 
    static final double two = 2.9; 
    void sub(int par) 
    { 
        switch(par) { 
            case one: 
                // ... 
            case (int)(two + 2): 
                break; 
        } 
    } 

 
Wyrażenie  
 

 

(int)(two + 2) 

 
jest wyrażeniem stałym o wartości 4 (sic!). 
 
 

Respektowanie nawiasów 

 
Jeœli wyra¿enie w nawiasach okr¹g³ych jest poprzedzone operatorem, to wykonanie operacji okreœlonej przez 
ten operator nast¹pi dopiero po opracowaniu wyra¿enia w nawiasach. 
 
Na przyk³ad 
 

a - (b + c) 

 
Operacja odejmowania zostanie wykonana dopiero po wykonaniu operacji dodawania. 
 
 

Uwzględnianie priorytetów 

 
Jeœli wyra¿enie jest argumentem dwóch operatorów o ró¿nym priorytecie, to najpierw wykonuje siê operacjê 
okreœlon¹ przez operator o wy¿szym priorytecie. 
 
Na przyk³ad 
 

a + b * c;        // a + (b * c); 

background image

 

195 

 
Najpierw zostanie wykonana operacja mno¿enia, a dopiero po niej operacja dodawania. 
 
 

Uwzględnianie wiązań 

 
Jeœli  wyra¿enie  mo¿e  byæ  uznane  za  argument  dwóch  operatorów  o  równym  priorytecie,  to  kolejnoœæ 
wykonania operacji jest okreœlona przez wi¹zanie
 
Jeœli wi¹zanie operatora jest lewe, to najpierw jest wykonywana operacja okreœlona przez operator znajduj¹cy 
siê z lewej strony wyra¿enia, a jeœli jest prawe, to najpierw z prawej strony. 
 
Na przyk³ad 
 

double num; 
int fix; 
num = fix = 4; 

 
Poniewa¿ operator przypisania ma wi¹zanie prawe, wiêc ostatnia instrukcja jest traktowana tak jak instrukcja 
 

num = (fix = 4); 

 
a ta jak para instrukcji 
 

fix = 4; 
num = fix; 

 
 

Realizowanie skutków ubocznych 

 
Skutkiem  ubocznym  opracowania  wyra¿enia  jest  wykonanie  przypisañ  i  przes³añ  danych.  Skutek  uboczny 
operacji musi byæ zrealizowany nie póŸniej ni¿ w najbli¿szym punkcie charakterystycznym.  
 
Punkt  charakterystyczny  programu  wystêpuje  tu¿  za  ka¿dym  kompletnym  wyra¿eniem  i  deklaratorem,  tu¿  po 
operatorach  
 
 

&& (koniunkcja), || (dysjunkcja

 
oraz tu¿ po znaku ? (pytajnik) w trójargumentowym operatorze ?:
 
Na przyk³ad 
 

int fix = 0; 
boolean bool = fix++ != 0 || fix != 0; 

 
W chwili opracowywania prawego argumentu operatora dysjunkcji zmienna fix ma wartoœæ 1
 
Gdyby przed tym operatorem nie wystêpowa³ punkt charakterystyczny, to mog³aby mieæ wartoœæ 0.
 
 
_________________________________________________________________________________________ 

Operacje 

 
Operacje na danych mogą być wykonywane za pomocą wyrażeń fabrykujących, albo za pomocą operatorów
Ponieważ  napisy  []  (nawias,  nawias),  (kropka),  ()  (nawias,  nawias)  i  new  nie  są  operatorami,  więc 
indeksowania  tablicy,  wybierania  składników  klasy,  wywoływania  procedur  oraz  zarządzania  pamięcią 
operacyjn
ą nie realizuje się  za pomocą operatorów. 
 
 

Indeksowanie tablicy 

background image

 

196 

 
Indeksowanie tablicy wyra¿a siê za pomoc¹ znaków [] (nawiasnawias). Rezultatem indeksowania jest element 
tablicy. Wi¹zanie indeksowania tablicy jest lewe
 

Np.    arr[10][20] == (arr[10])[20] 

 
Operacja indeksowania ma postaæ 
 

 

ref[ind

 
w której ref jest odnoœnikiem do tablicy, a ind jest wyra¿eniem ca³kowitym.  
 
Wyra¿enie ref[ind] jest l-nazw¹ tego elementu tablicy identyfikowanej przez ref, który ma indeks ind
 
Na przyk³ad 
 

int vec[] = { 10, 20, 30 }; 

 
Zmienna  vec  jest  odnoœnikiem  do  3-elementowego  wektora.  Wyra¿enie  vec[0]  jest  nazw¹  pierwszego,  a 
wyra¿enie vec[2] jest nazw¹ ostatniego elementu tego wektora.
 
 
 

Wybór składnika 

 
Wybór sk³adnika klasy wyra¿a siê za pomoc¹ znaku . (kropka). Rezultat wyboru pola jest zmienn¹ opisan¹ przez 
to pole. Wi¹zanie wyboru sk³adnika jest lewe
 

Np. 

var.fun().var   == (var.fun()).var 

 
Operacja wyboru ma postaæ 
 

 

ref.name 

 
w której ref jest odnoœnikiem do obiektu, a name jest identyfikatorem jego sk³adnika. 
 
Wyra¿enie ref.name jest l-nazw¹ sk³adnika obiektu identyfikowanego przez ref.  
 
Na przyk³ad 
 

class Child { 
    String name; 
    int age = 0; 
    Child(String name, int age) 
    { 
        this.name = name; 
        this.age = age; 
    } 
    void setAge(int name) 
    { 
        this.age = age; 
    } 
    void sub() 
    { 
        Child tom = new Child("Thomas", 12), 
              bob = new Child("Robert", 0) ; 
        // ... 
        System.out.println(tom.name); 
        bob.setAge(12); 
        // ... 
    } 

 
 

background image

 

197 

Wywołanie procedury 

 
Wywo³anie  procedury  wyra¿a  siê  za  pomoc¹  znaków  ()  (nawias,  nawias).  Jeœli  typ  procedury  jest  ró¿ny  od 
"void", to istnieje jej rezultat
 

Np.    sub(x, y).z = 2; 

 
Operacja wywo³ania procedury ma postaæ 
 

 

ref.proc(argarg, ... , arg

 
w  której  ref  jest  odnoœnikiem  identyfikuj¹cym  obiekt  klasy,  zawieraj¹cej  procedurê  proc,  a  ka¿de  arg  jest 
argumentem wywo³ania. 
 
Rezultatem  wywo³ania  procedury  typu  ró¿nego  od  "void"  jest  zmienna  zainicjowana  wartoœci¹  okreœlon¹  w 
instrukcji powrotu. 
 
Na przyk³ad 
 

class Master { 
    int fix; 
    Master met() 
    { 
        return new Master(); 
    } 
    void sub() 
    { 
        met().fix = 12; 
        met() = null;     // bł

ą

d (met() nie jest l-nazw

ą

    } 

 
Typ rezultatu metody met jest odnoœnikowy. Wywo³anie met() jest nazw¹ zmiennej, ale nie jest l-nazw¹. 
 
 

Zarządzanie pamięcią operacyjną 

 
Zarz¹dzanie  pamiêci¹  operacyjn¹  odbywa  siê  za  pomoc¹  operacji  new.  Wykonanie  operacji  new  powoduje 
przydzielenie obszaru pamiêci na stercie. Zwolnienie obszaru dokona siê automatycznie, ale nie wczeœniej ni¿ 
po tym, gdy ani jeden odnoœnik nie bêdzie ju¿ identyfikowa³ tego obszaru. 
 
 

Przydzielenie obiektu 

 
Operacja przydzielenia obiektu ma postaæ 
 

 

new Class(argarg, ... , arg

 
w której Class jest nazw¹ klasy, a ka¿de arg jest argumentem jej konstruktora. 
 
Rezultatem operacji przydzielenia jest odnoœnik do w³aœnie przydzielonego obiektu. 
 
Na przyk³ad 
 

String ref = new String("Hello"); 
ref = new String(); 

 
 

Przydzielenie tablicy 

 
Operacja przydzielenia tablicy ma postaæ 

background image

 

198 

 

 

new Type [exp][exp] ... [exp

 
w  której  Type  jest  identyfikatorem  typu  (np.  "int"  albo  "String"),  a  ka¿de  exp  jest  wyra¿eniem  ca³kowitym 
(jeœli pewne z nich jest puste, to wszystkie nastêpuj¹ce po nim tak¿e musz¹ byæ puste). 
 
Rezultatem operacji przydzielenia tablicy jest odnoœnik do w³aœnie przydzielonej tablicy. 
 
Na przyk³ad 
 

int vec[] = new int [2]; 
vec = new int [4]; 
String arr[][] = new String [3][]; 
arr = new String [3][4]; 

 
Po wykonaniu instrukcji 
 

int vec[] = new int [2]; 

 
odnoœnik vec identyfikuje 4-elementow¹ tablicê zmiennych typu "int". 
 
Po wykonaniu instrukcji 
 

String arr[][] = new String [3][]; 

 
odnoœnik  arr  identyfikuje  3-elementow¹  tablicê  odnoœników  do  tablic  odnoœników  do  obiektów  klasy  String 
(ka¿dy z 3 odnoœników tablicy ma wartoœæ null). 
 
Po wykonaniu instrukcji 
 

arr = new String [3][4]; 

 
odnoœnik  arr  identyfikuje  3-elementow¹  tablicê  odnoœników  do  4-elementowych  tablic  odnoœników  do 
obiektów klasy String (ka¿dy z 3 odnoœników ma wartoœæ ró¿n¹ od null, ale ka¿dy z pozosta³ych ma wartoœæ 
null). 
 
 
_______________________________________________________________________________________ 

Operatory 

 
Wykaz operatorów języka, z podaniem ich priorytetów i wiązań znajduje się w Dodatku Priorytety operatorów.  
 
Rezultatem  każdej  z  wymienionych  tu  operacji  jest  zmienna.  Każde  wyrażenie  określone  przez  operację  jest 
chwilową nazwą tej zmiennej. To, czy jest l-nazwą wynika z opisu operacji. 
 
 

Operatory konwersji 

 
Operatorem konwersji jest (Type). Wi¹zanie operatora konwersji jest prawe
 

Np.    (double)(int)12.8  ==  (double)((int)12.8) 

 
Operacja konwersji ma postaæ 
 

 

(Type)exp 

 
w której Type jest nazw¹ typu docelowego, a exp jest wyra¿eniem poddawanym konwersji. 
 
Rezultatem konwersji jest zmienna typu "Type", zainicjowana wartoœci¹ wyra¿enia exp po przekszta³ceniu jej 
do typu "Type".  

background image

 

199 

 
Uwaga:  Jeœli  typem  docelowym  jest  tablica  odnoœników,  to  w  nazwie  typu  nie  mog¹  wyst¹piæ  rozmiary 
tablicy. 
 
Na przyk³ad 
 

String str[] = { "Hello", "World" }; 
Object obj[] = (Object [])str; 
System.out.print((String)obj[0]);     // Hello 
system.out.print(obj[1]);             // World 

 
 

Konwersje nieodnośnikowe 

 
Rezultatem konwersji nieodnoœnikowej (np. z typu "double" do "int") jest zmienna typu "Type", zainicjowana 
wartoœci¹ wyra¿enia exp po przekszta³ceniu jej do typu "Type".  
 
Na przyk³ad 
 

System.out.print(12.8);               // 12.8 
System.out.print((double)(int)12.8);  // 12                       

 
 

Konwersje odnośnikowe 

 
Konwersja  odnoœnikowa  jest  poprawna  tylko  wówczas,  gdy  jest  dopuszczalna  i  wykonalna.  Rezultatem 
poprawnej konwersji odnoœnikowej (np. z typu "Vector" do "Object") jest odnoœnik zainicjowany wartoœci¹ 
argumentu.  
 
Konwersja odnoœnikowa jest dopuszczalna tylko wówczas, gdy jest to¿samoœciowa (np. z typu "Vector" do 
"Vector")  albo  gdy  polega  na  przekszta³ceniu  z  klasy  do  nadklasy  (np.  z  "Vector"  do  "Object"),  z  klasy  do 
podklasy (np. z "Object" do "Vector") albo z klasy do implementowanego przez ni¹ interfejsu (np. z "Vector
do "Cloneable"). 
 
Konwersja odnoœnikowa jest wykonalna tylko wówczas, gdy jest dopuszczalna, a wyra¿enie 
 

 

exp instanceof Type 

 
(por. opis operatora instanceof) ma wartoœæ true
 
Na przyk³ad 
 

String city = "Warsaw"; 
Object obj = city;         // konwersja niejawna 
city = (String)obj;        // konwersja jawna 
Vector vec = (Vector)obj;  // bł

ą

d (konwersja niewykonalna) 

 
 

Operatory zwiększenia i zmniejszenia 

 
Operatorem  zwiêkszenia  jest  ++  (plus,  plus),  a  operatorem  zmniejszenia  jest  --(minus,  minus).  Ka¿dy  z  nich 
mo¿e  wyst¹piæ  w  postaci  przyrostkowej  albo  przedrostkowej.  Wi¹zanie  operatora  przyrostkowego  jest  lewe,  a 
przedrostkowego prawe.  
 
Argument operacji zwiêkszenia i zmniejszenia musi byæ l-nazw¹ zmiennej. 
 

Np. 

fix-- 

 

++++fix  ==  ++(++fix)    // bł

ą

d (++fix nie jest l-nazw

ą

 
 

background image

 

200 

Operacje przyrostkowe 

 
Przyrostkowa operacja zwiêkszenia ma postaæ 
 

 

var++ 

 
a przyrostkowa operacja zmniejszenia ma postaæ 
 

 

var-- 

 
w których var jest wyra¿eniem arytmetycznym (m.in. typu "char"). 
 
Wyra¿enie  var++  jest  nazw¹  zmiennej  tymczasowej  zainicjowanej  wyra¿eniem  var.  Skutkiem  ubocznym 
wykonania operacji jest zwiêkszenie wartoœci zmiennej var o 1
 
Wyra¿enie var-- jest nazw¹ zmiennej zainicjowanej wyra¿eniem var. Skutkiem ubocznym wykonania operacji 
jest zmniejszenie wartoœci zmiennej var o 1
 
Na przyk³ad 
 

int fix = 12; 
int fix1 = fix--; 
System.out.print(fix);     // 11 
System.out.print(fix1);    // 12 

 
 

Operacje przedrostkowe 

 
Przedrostkowa operacja zwiêkszenia ma postaæ 
 

 

++var 

 
a przedrostkowa operacja zmniejszenia ma postaæ 
 

 

--var     

 
w których var jest wyra¿eniem arytmetycznym (m.in. typu "char"). 
 
Wyra¿enie  ++var  jest  l-nazw¹  zmiennej  var.  Skutkiem  ubocznym  wykonania  operacji  jest  zwiêkszenie 
wartoœci zmiennej var o 1
 
Wyra¿enie  --var  jest  l-nazw¹  zmiennej  var.  Skutkiem  ubocznym  wykonania  operacji  jest  zmniejszenie 
wartoœci zmiennej var o 1
 
Na przyk³ad 
 

int fix = 10; 
int fix1 = ++fix; 
System.out.print(fix);     // 11 
System.out.print(fix1);    // 11 

 
 

Operatory zachowania i zmiany znaku 

 
Operatorami zachowania i zmiany znaku s¹ + (plus) i - (minus). Wi¹zanie tych operatorów jest prawe
 

Np.    -+var   == -(+var) 

 
Operacja zachowania znaku ma postaæ 
 

background image

 

201 

 

+exp 

 
a operacja zmiany znaku ma postaæ 
 

 

-exp 

 
w których exp jest wyra¿eniem arytmetycznym. 
 
Wyra¿enie +exp oraz wyra¿enie -exp jest nazw¹ zmiennej takiego samego typu jakiego jest wyra¿enie exp, ale 
po poddaniu go promocjom (np. z typu "char" do "int"). 
 
Na przyk³ad 
 

char fix = '2'; 
System.out.print(fix);           // 2 
System.out.print(+fix);          // 50 (kod cyfry 2) 

 
 

Operatory czynnikowe 

 
Operatorami  czynnikowymi  s¹  *  (gwiazdka),  /  (skoœnik)  i  %  (procent).  Operatory  czynnikowe  s¹  u¿ywane  w 
operacjach mno¿enia, dzielenia i wyznaczania reszty z dzielenia. Wi¹zanie operatorów czynnikowych jest lewe
 

Np.    a / b * c  == (a / b) * c 

 
 

Operator * 

 
Operacja mno¿enia ma postaæ 
 

 

expL expR 

 
w której expL i expR s¹ wyra¿eniami arytmetycznymi. 
 
Rezultat mno¿enia jest nazw¹ zmiennej, zainicjowanej iloczynem argumentów. 
 
Na przyk³ad 
 

System.out.print('#' * 2);         // 70 

 
 

Operator / 

 
Operacja dzielenia ma postaæ 
 

 

expL expR 

 
w której expL i expR s¹ wyra¿eniami arytmetycznymi. 
 
Rezultat  dzielenia  jest  nazw¹  tymczasowej  zmiennej,  zainicjowanej  ilorazem  lewego  i  prawego  argumentu. 
Jeœli oba argumenty s¹ ca³kowite, to rezultat tak¿e jest ca³kowity. 
 
Na przyk³ad 
 

System.out.print(5.0 / 2);         // 2.5 
System.out.print(5 / 2);           // 2 
System.out.print(1 / 2 * 4);       // 0 
System.out.print(1. / 2 * 4);      // 2 

 
 

background image

 

202 

Operator % 

 
Operacja wyznaczenia reszty z dzielenia ma postaæ 
 

 

expL expR  

 
w której expL i expR s¹ wyra¿eniami arytmetycznymi ca³kowitymi
 
Rezultat  wyznaczenia  reszty  jest  nazw¹  zmiennej  zainicjowanej  reszt¹  z  dzielenia  lewego  argumentu  przez 
prawy. Znak reszty jest taki sam jak znak lewego argumentu. 
 
Przyjmuje siê z definicji, ¿e 
 

 

== - (b) * b 

 
Na przyk³ad 
 

System.out.print(14 % 3);       // 2 
System.out.print(14 % -3);      // 2 
System.out.print(-14 % 3);      // -2 

 
 

Operatory składnikowe 

 
Operatorami  sk³adnikowymi  s¹  +  (plus)  i  -  (minus).  Operatory  sk³adnikowe  s¹  u¿ywane  w  operacjach 
dodawania i odejmowania. Wi¹zanie operatorów sk³adnikowych jest lewe
 

Np.    a - b - c  ==  (a - b) - c 

 
 

Operator + 

 
Operacja dodawania ma postaæ 
 

 

expL expR 

 
w której expL i expR s¹ wyra¿eniami arytmetycznymi, znakowymi albo ³añcuchowymi. 
 
Rezultat dodawania jest nazw¹ zmiennej, zainicjowanej sum¹ argumentów.  
 
Na przyk³ad 
 

System.out.print(2 + 3);             // 5 
System.out.print('#' + 1);           // 36 
System.out.print('a' + 0);           // 97 
System.out.print('a' + "0");         // a0 

 

Operator - 

 
Operacja odejmowania ma postaæ 
 

 

expL expR 

 
w której expL i expR s¹ wyra¿eniami arytmetycznymi. 
 
Rezultat odejmowania jest nazw¹ zmiennej, zainicjowanej ró¿nic¹ lewego i prawego argumentu. 
 
Na przyk³ad 
 

System.out.print(2 - 3);             // -1 

background image

 

203 

System.out.print('z' - 'a');         // 25 

 
 

Operator pochodzenia 

 
Operatorem pochodzenia jest instanceof. Wiązanie operatora przynależności jest lewe
 

Np. 

((Object)new String()) instanceof String 

 
Operacja pochodzenia ma postać 
 

 

exp instanceof Class 

 
w której exp jest wyrażeniem odnośnikowym, a Class jest identyfikatorem klasy. 
 
Rezultatem  operacji  pochodzenia  jest  orzecznik,  który  ma  wartośc  true  tylko  wówczas,  gdy  odnośnik  exp 
identyfikuje obiekt klasy Class albo jej podklasy. 
 
 
Na przykład 
 

class Person { 
    // ... 

 
class Woman extends Person { 
    // ... 

 
class Master { 
    static void sub(Person person) 
    { 
        if(person instanceof Woman) { 
            Woman woman = (Woman)person; 
            // ... 
        } 
    } 

 
Instrukcja 
 

Woman woman = (Woman)person; 

 
zostanie wykonana tylko wówczas, gdy odnośnik person identyfikuje obiekt klasy Woman
 
Tak się stanie, gdy funkcja sub zostanie wywołana za pomocą instrukcji 
 

Master.sub(new Woman()); 

 
ale nie stanie się, jeśli zostanie wywołana za pomocą instrukcji 
 

Master.sub(new Person()); 

 
 

Operatory porównania 

 
Operatorami porównania s¹: < (mniejsze), > (wiêksze), <= (mniejszerówne), >= (wiêkszerówne), == (równe), 
!=  (nie  równe).  Dwa  ostatnie  operatory  maj¹  priorytet  ni¿szy  ni¿  pozosta³e.  Wi¹zanie  operatorów  porównania 
jest lewe
 

Np.    a < b < c  == (a < b) < c 

 
Operacja porównania ma postaæ 

background image

 

204 

 

 

expL expR 

 
w której @ jest dowolnym operatorem porównania, a expL i expR s¹ wyra¿eniami: oba arytmetycznymioba 
orzecznikowymi
, albo oba odnoœnikowymi
 
Rezultatem  operacji  porównania  jest  zmienna  orzecznikowa  o  wartoœci  true  jeœli  relacja  okreœlona  przez 
porównanie jest prawdziwa, albo zmienna o wartoœci false jeœli relacja jest fa³szywa
 
 

Porównania arytmetyczne 

 
Porównanie  zmiennych  arytmetycznych  (w  tym  zmiennych  typu  "char")  polega  na  porównaniu  ich  wartoœci 
liczbowych.  
 
Na przyk³ad 
 

System.out.print(1.2 >= 1);          // true 

 
 

Porównania orzecznikowe 

 
Porównanie zmiennych orzecznikowych polega na porównaniu ich wartoœci logicznych.  Zezwala siê na u¿ycie 
tylko operatorów równe (==) i nie-równe (!=). 
 
Na przyk³ad 
 

boolean flag = true; 
System.out.print(flag != false);     // true 

 
 

Porównania odnośnikowe 

 
Porównania  odnoœników  mog¹  byæ  tylko  na  równoœæ  (==)  i  nie-równoœæ  (!=).  Jeœli  oba  odnoœniki 
identyfikuj¹ ten sam obiekt (w szczególnoœci tablicê), to rezultat porównania na równoœæ ma wartoœæ true, a 
w przeciwnym razie ma wartoœæ false. Analogicznie definiuje siê porównanie na nie-równoœæ. 
 
Na przyk³ad 
 

String one = "Hello" + "World", 
       two = "HelloWorld";    
System.out.print(one == two);        // true 
System.out.print(one + "" != one);   // true 

 
 

Operatory logiczne 

 
Operatorami  logicznymi  s¹:  !  (zaprzeczenie),  &&  (koniunkcja)  i  ||  (dysjunkcja).  Najwy¿szy  priorytet  ma 
zaprzeczenie,  a  najni¿szy  dysjunkcja.  Wi¹zanie  operatora  zaprzeczenia  jest  prawe,  a  pozosta³ych  operatorów 
lewe
 

Np.    a || !b && c  ==  a || ((!b) && c) 

 
 

Operator ! 

 
Operacja zaprzeczenia ma postaæ 
 

 

!exp 

 

background image

 

205 

w której exp jest wyra¿eniem orzecznikowym
 
Rezultatem  operacji  zaprzeczenia  jest  zmienna  orzecznikowa.  Zmienna  ta  ma  wartoœæ  true  tylko  wówczas, 
gdy wyra¿enie ma wartoœæ false
 
Na przyk³ad 
 

boolean flag = true; 
System.out.print(!flag);             // false 
System.out.print(!(12 > 3);          // false 

 
 

Operator && 

 
Operacja koniunkcji ma postaæ 
 

 

expL && expR 

 
w której expL i expR s¹ dowolnymi wyra¿eniami orzecznikowymi. 
 
Rezultatem  koniunkcji  jest  zmienna  orzecznikowa.  Zmienna  ta  ma  wartoœæ  true  tylko  wówczas,  gdy  oba 
wyra¿enia maj¹ wartoœæ true.  
 
Uwaga: Tu¿ przed operatorem && wystêpuje punkt charakterystyczny. Jeœli wyra¿enie expL ma wartoœæ 
false, to rezultat koniunkcji jest znany, a wiêc nie opracowuje siê wyra¿enia expR
 
Na przyk³ad 
 

int vec[] = { 10, 20, 30 }; 
int chr = System.in.read(); 
boolean flag = chr >= '0' && chr <= '2'; 
if(flag && chr == vec[chr - 'a']) 
    System.out.println(); 

 
Operacja == jest wykonywana tylko wówczas, gdy zmienna flag ma wartoœæ true
 
 

Operator || 

 
Operacja dysjunkcji ma postaæ 
 

 

expL || expR 

 
w której expL i expR s¹ dowolnymi wyra¿eniami orzecznikowymi. 
 
Rezultatem  dysjunkcji  jest  zmienna  orzecznikowa.  Zmienna  ta  ma  wartoœæ  false  tylko  wówczas,  gdy  oba 
wyra¿enia maj¹ wartoœæ false
 
Uwaga:  Tu¿  przed  operatorem  ||  wystêpuje  punkt  charakterystyczny.  Jeœli  wyra¿enie  expL  ma  wartoœæ 
true, to rezultat dysjunkcji jest znany, a wiêc nie opracowuje siê wyra¿enia expR
 
Na przyk³ad 
 

int vec[] = { 10, 20, 30 }; 
int chr = System.in.read(); 
boolean flag = chr < '0' && chr > '2'; 
if(flag || chr == vec[chr - 'a']) 
    System.out.println(); 

 
Operacja == jest wykonywana tylko wówczas, gdy zmienna flag ma wartoœæ false
 

background image

 

206 

 

Operatory bitowe 

 
Operatorami bitowymi s¹: (zanegowanie bitów), & (iloczyn bitów), | (suma bitów), ^ (suma modulo 2 bitów), 
<<  (przesuniêcie  bitów  w  lewo),  >>  (przesuniêcie  bitów  w  prawo),  >>>  (przesuniêcie  bitów  w  prawo-bez-
znaku
). Najwy¿szy priorytet ma zanegowanie, ni¿szy przesuniêcia, a potem kolejno: iloczynsuma modulo 2  i 
suma
. Wi¹zanie operatora zanegowania bitów jest prawe, a pozosta³ych lewe
 

Np.    a || !b && c << d ==  a || ((!b) && (c << d)) 

 
 

Operator ~ 

 
Operacja zanegowania bitów ma postaæ 
 

 

~exp 

 
w której exp jest wyra¿eniem ca³kowitym. 
 
Rezultatem  operacji  zanegowania  bitów  jest  zmienna  takiego  samego  typu  jak  exp,  po  poddaniu  jej  
promocjom, a nastêpnie zanegowaniu ka¿dego jej bitu. 
 
Uwaga: Negacj¹ bitu 1 jest bit 0, a negacj¹ bitu 0 jest bit 1
 
Na przyk³ad 
 

int red = 1, green = 2, blue = 4; 
int hue = red | green;        // 00 ... 011 (kolor 

ż

ółty) 

hue = ~hue;                   // 11 ... 100 (kolor niebieski) 

 
Trzy  najmniej  znacz¹ce  bity  zmiennej  hue  reprezentuj¹  jeden  z  8  kolorów.  Wykonanie  operacji  zanegowania 
bitów powoduje zmianê koloru na dope³niaj¹cy.
 
 
 

Operator & 

 
Operacja iloczynu bitów ma postaæ 
 

 

expL expR 

 
w której expL i expR s¹ wyra¿eniami ca³kowitymi. 
 
W  celu  utworzenia  wyniku  operacji,  zmienne  expL  i  expR  poddaje  siê  konwersjom  do  typu  wspólnego,  a 
nastêpnie  ka¿dy  bit  wyniku  tworzy  siê  z  odpowiadaj¹cych  sobie  bitów  argumentów  wyznaczaj¹c  ich  iloczyn 
logiczny
.  
 
Uwaga: Iloczyn logiczny pary bitów ma wartoœæ 1 tylko wówczas gdy oba bity s¹ jedynkowe
 
Na przyk³ad 
 

int fix = 6;                  //    00 ... 110 
int mask = '\u0003';          //    00 ... 011 
fix = fix & ~mask;              
System.out.print(fix);        // 4 (00 ... 100) 

 
Wykonanie operacji na zmiennej  fix powoduje wyzerowanie tych wszystkich jej bitów, które w masce mask s¹ 
jedynkowe.
 
 
 

Operator ^ 

background image

 

207 

 
Operacja sumy modulo 2 bitów ma postaæ 
 

 

expL expR 

 
w której expL i expR s¹ wyra¿eniami ca³kowitymi. 
 
W  celu  utworzenia  wyniku  operacji,  zmienne  expL  i  expR  poddaje  siê  konwersjom  do  typu  wspólnego,  a 
nastêpnie  ka¿dy  bit  wyniku  tworzy  siê  z  odpowiadaj¹cych  sobie  bitów  argumentów  wyznaczaj¹c  ich  sumê 
logiczn¹ modulo 2

 
Uwaga: Suma logiczna modulo 2 pary bitów ma wartoœæ 1 tylko wówczas gdy bity s¹ ró¿ne
 
Na przyk³ad 
 

int fix = 6;                  //    00 ... 110 
int mask = '\u0003';          //    00 ... 011 
fix = fix ^ mask;              
System.out.print(fix);        // 5 (00 ... 101) 

 
Wykonanie operacji na zmiennej fix powoduje zanegowanie tych wszystkich jej bitów, które w masce mask s¹ 
jedynkowe.
 
 
 

Operator | 

 
Operacja sumy bitów ma postaæ 
 

 

expL expR 

 
w której expL i expR s¹ wyra¿eniami ca³kowitymi. 
 
W  celu  utworzenia  wyniku  operacji,  zmienne  expL  i  expR  poddaje  siê  konwersjom  do  typu  wspólnego,  a 
nastêpnie  ka¿dy  bit  wyniku  tworzy  siê  z  odpowiadaj¹cych  sobie  bitów  argumentów  wyznaczaj¹c  ich  sumê 
logiczn¹
.  
 
Uwaga: Suma logiczna pary bitów ma wartoœæ 0 tylko wówczas gdy oba bity s¹ zerowe
 
 
Na przyk³ad 
 

int fix = 5;                  //    00 ... 101 
int mask = '\u0003';          //    00 ... 011 
fix = fix | mask;              
System.out.print(fix);        // 7 (00 ... 111) 

 
Wykonanie  operacji  na  zmiennej  fix  powoduje  ustawienie  tych  wszystkich  jej  bitów,  które  w  masce  mask  s¹ 
jedynkowe
.
 
 
 

Operator << 

 
Operacja przesuniêcia bitów w lewo ma postaæ 
 

 

expL << N 

 
w której expL i N s¹ wyra¿eniami ca³kowitymi. 
 
W celu utworzenia wyniku operacji, zmienn¹ expL poddaje siê promocji, a nastêpnie ka¿dy bit wyniku tworzy 
siê z bitów tej nowej zmiennej po przesuniêciu ich o N pozycji w lewo

background image

 

208 

 
Uwaga: Podczas przesuwania w lewo bity najbardziej znacz¹ce s¹ odrzucane, a na pozycje najmniej znacz¹ce 
wchodz¹ bity 0
 
Na przyk³ad 
 

int fix = 7;                  // 00 ... 0111 
fix = fix << 2; 
System.out.print(fix);        // 28 (00 ... 011100) 

 
Bity zmiennej fix przesuniêto o 2 pozycje w lewo. 
 
 

Operator >> 

 
Operacja przesuniêcia bitów w prawo ma postaæ 
 

 

expL >> N 

 
w której expL i N s¹ wyra¿eniami ca³kowitymi. 
 
W celu utworzenia wyniku operacji, zmienn¹ expL poddaje siê promocji, a nastêpnie ka¿dy bit wyniku tworzy 
siê z bitów tej nowej zmiennej po przesuniêciu ich o N pozycji w prawo
 
Uwaga:  Podczas  przesuwania  w  prawo  bity  najmniej  znacz¹ce  s¹  odrzucane,  a  bit  najbardziej  znacz¹cy  nie 
ulega zmianie. 
 
Na przyk³ad 
 

int fix = 15;                //    00 ... 01111 
fix = fix >> 2; 
System.out.print(fix);       // 3 (00 ... 00011) 

 
Bity zmiennej fix przesuniêto o 2 pozycje w prawo. 
 
 

Operator >>> 

 
Operacja przesuniêcia bitów w-prawo-bez-znaku ma postaæ 
 

 

expL >>> N 

 
w której expL i N s¹ wyra¿eniami ca³kowitymi. 
 
W celu utworzenia wyniku operacji, zmienn¹ expL poddaje siê promocji, a nastêpnie ka¿dy bit wyniku tworzy 
siê z bitów tej nowej zmiennej po przesuniêciu ich o N pozycji w prawo
 
Uwaga:  Podczas  przesuwania  w  prawo  bity  najmniej  znacz¹ce  s¹  odrzucane,  a  bit  najbardziej  znacz¹cy  jest 
zerowany. 
 
Na przyk³ad 
 

int fix = -1;                 //   111 ... 111 
fix = fix >>> 30; 
System.out.print(fix);        //3 (000 ... 011)     

 
Bity zmiennej fix przesuniêto o 2 pozycje w prawo. 
 
 

Operatory warunku 

background image

 

209 

 
Operatorem warunku jest ?: (pytajnikdwukropek). Wi¹zanie operatora warunku jest prawe.  
 

Np.    a ? b : c ? d : e  == a ? b : (c ? d : e) 

 
Operacja warunku ma postaæ 
 

 

exp expT expF 

 
w której exp  jest wyra¿eniem orzecznikowym, a expT i expF s¹ wyra¿eniami dowolnego typu. 
 
Uwaga: Bezpoœrednio za pytajnikiem wystêpuje punkt charakterystyczny
 
Wyra¿enie warunkowe jest nazw¹ zmiennej typu wspólnego "Type" wyra¿eñ expT i expF o wartoœci 
 
 

(Type)expT    dla exp o wartoœci true 

 

(Type)expF    dla exp o wartoœci false 

 
Uwaga:  W  ka¿dym  przypadku  jest  opracowywane  wyra¿enie  exp  oraz  co  dok³adnie  jedno  z  wyra¿eñ  expT  i 
expF
 
Na przyk³ad 
 

int fix, fix1 = 10, fix2 = 20;                            
fix = System.in.read(); 
fix = (fix > 0 ? fix1 : fix2); 
fix = fix > 0 ? fix1 : fix2;     // identyczne z poprzednim 
System.out.print(fix);           // 10 (sic!) 
(fix > 0 ? fix1 : fix2) = 4;     // bł

ą

d (wymagana l-nazwa) 

 
 

Operatory przypisania 

 
Operatorem  przypisania  jest  =  (równa  siê)  oraz  ka¿dy  operator  o  postaci  @=  w  której  @  jest  jednym  z 
nastêpuj¹cych operatorów 
 

 

*  /  %  +  -  <<  >>  ^  &  |   

 
Wi¹zanie operacji przypisania jest prawe
 

Np.    a = b += c  ==  a = (b += c) 

 
 

Operator = 

 
Prosta operacja przypisania ma postaæ 
 

 

var exp 

 
w której var jest l-nazw¹ zmiennej, a exp jest wyra¿eniem. 
 
Na przyk³ad 
 

int fix = 10;      // zainicjowanie 
fix = 20;          // przypisanie 
++(fix = 20);      // bł

ą

d (fix = 20 nie jest l-nazw

ą

 
Jeœli  var  jest  typu  arytmetycznego,  to  exp  musi  byæ  typu  arytmetycznego.  Jeœli  jest  typu  odnoœnikowego 
Class, to wyra¿enie exp musi identyfikowaæ obiekt klasy Class albo jej podklasy. 
 

background image

 

210 

Wykonanie  prostej  operacji  przypisania  sk³ada  siê  z  wyznaczenia  wartoœci  wyra¿enia  exp,  poddania  jej 
ewentualnej konwersji do typu zmiennej var, a nastêpnie przypisania zmiennej var
 
Uwaga: Jeœli  typ  wyra¿enia  exp  jest ró¿ny od typu zmiennej var, to wymaga siê, aby konwersja mog³a byæ 
wykonana niejawnie
 
Na przyk³ad 
 

int fix; 
fix = 12; 
fix = (int)4.8;     
fix = 4.8;               // bł

ą

d (brak konwersji)  

String name = "Isabel"; 
Object obj; 
obj = name;              // obj = (Object)name; 
name = (String)obj; 
name = obj;              // bł

ą

d (brak konwersji) 

 
 

Operatory @= 

 
Rozszerzona operacja przypisania ma postaæ 
 

 

var @= exp 

 
Jest ona równowa¿na operacji  
 

 

var var exp 

 
ale tylko wówczas gdy opracowanie wyra¿enia var jest jednokrotne.  
 
Na przyk³ad 
 

int arr[] = { 10, 20 }, fix = 0; 
arr[fix += 1] += 3;   // fix = fix + 1; arr[1] = arr[1] + 3; 
System.out.print(fix);      // 1 

 
Instrukcja  
 

arr[fix += 1] += 3;                 // zwi

ę

kszenie fix o 1 

 
jest wykonywana tak jak  
 

fix += 1; 
arr[fix] = arr[fix] + 3; 

 
 a wiêc nie jest równowa¿na instrukcji 
 

arr[fix += 1] = arr[fix += 1] + 3;  // zwi

ę

kszenie fix o 2