POO – Laborator 5

Notiuni de proiectare OO. Fire de executie (threads) Java. Interfete grafice cu utilizatorul (GUI). Tratarea evenimentelor

 

5.1. Descrierea laboratorului

 

In aceasta lucrare de laborator vor fi acoperite urmatoarele probleme:

- Utilizarea firelor de executie (clasa Threads) Introducere in threadslink extern

- Interfete grafice cu utilizatorulInterfete grafice swing link extern

- Precizari privind « colocviul » la laborator

 

 

5.2. Utilizarea firelor de executie (threads) in Java

 

5.2.1. Modalitati de a crea un fir de executie

 

Pentru a crea un nou fir de executie (detalii privind firele de executie - threads - in Java) exista doua modalitati.

 

1. Se poate declara o clasa ca subclasa a clasei Thread, subclasa care trebuie sa rescrie codul (override) metodei run() a clasei Thread (care nu contine nici un cod), noul fir de executie fiind creat prin alocarea si lansarea unei instante a subclasei.

 

Formatul pentru crearea unei subclase care extinde clasa Thread si ii reimplementeaza metoda run() este urmatorul:

                       

     class FirT extends Thread {
        public void run() {
                              // codul firului de executie
        }
     }

 

Formatul pentru crearea unei instante a subclasei este urmatorul:

                       

       FirT fir = new FirT();

 

2. Se poate declara o clasa care implementeaza interfata Runnable, interfata care contine doar declaratia unei metode run() (declaratie similara celei din clasa Thread, clasa Thread implementand interfata Runnable), noul fir fiind obtinut prin crearea unei instante a noii clase, pasata constructorului la crearea unei instante a clasei Thread, si lansarea noii instante a clasei Thread.

 

Formatul pentru crearea unei clase care implementeaza interfata Runnable si ii implementeaza metoda run() este urmatorul:

                       

     class FirR implements Runnable {
        public void run() {
                              // codul firului de executie
        }
     }

Formatul pentru crearea unei instante a noii clase si apoi a unei instante a clasei Thread este urmatorul:

       FirR r = new FirR();
       Thread fir = new Thread(r);

 

In ambele cazuri formatul pentru lansarea noului fir de executie, este urmatorul:

                       

      fir.start();    

 

Variante compacte pentru crearea si lansarea noilor fire de executie:

                       

       new FirT().start();                   // nu exista variabila de tip FirT 
                                             // care sa refere explicit firul
       FirR r = new FirR();
       new Thread(r).start();                // nu exista variabila de tip Thread 
                                             // care sa refere explicit firul
       Thread fir = new Thread(new FirR());  // nu exista variabila de tip FirR
       Fir.start();                          // care sa refere explicit firul                                        
       new Thread(new FirR()).start();       // nu exista variabila de tip Thread
                                             // si nici variabila de tip FirR 
                                             // care sa refere explicit firul 

 

5.2.2. Programe de lucru cu fire de executie

 

Urmatorul program va fi folosit pentru a exeplifica lucrul cu fire de executie. Clasa FirSimplu extinde clasa Thread, iar metoda principala lanseaza metoda run() ca nou fir de executie.

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

  public class FirSimplu extends Thread { // obiectele din clasa curenta sunt thread-uri

    public FirSimplu(String str) {

        super(str);                       // invocarea constructorului Thread(String)

    }                                     // al superclasei Thread

    public void run() {                   // “metoda principala” a thread-ului curent

        for (int i = 0; i < 5; i++) {

            System.out.println(i + " " + getName()); // obtinerea numelui thread-ului

            try {

                sleep((long)(Math.random() * 1000)); // thread-ul “doarme” 0...1 secunda

            } catch (InterruptedException e) {}

        }

        System.out.println("Gata! " + getName());    // obtinerea numelui thread-ului

    }

    public static void main (String[] args) {

        new FirSimplu("Unu").start();         // “lansarea” thread-ului (apeleaza run())

    }

  }

Executia FirSimplu:       2 executii DemoDouaFire:      

 

Clasa DemoDouaFire lanseaza doua fire de executie de tip FirSimplu executate concurent.

 

1

2

3

4

5

6

 public class DemoDouaFire {

    public static void main (String[] args) {

        new FirSimplu("Unu").start();     // “lansarea” primului thread

        new FirSimplu("Doi").start();     // “lansarea” celui de-al doilea thread

    }

 }

 

Firele au evolutii diferite, in functie de durata intarzierii introdusa in FirSimplu de linia:

 

          sleep( (long) (Math.random() * 1000) );

 

Clasa DemoTreiFire lanseaza trei fire de executie de tip FirSimplu executate concurent.

 

1

2

3

4

5

6

7

 public class DemoTreiFire {

    public static void main (String[] args) {

        new FirSimplu("Unu").start();     // “lansarea” primului thread

        new FirSimplu("Doi").start();     // “lansarea” celui de-al doilea thread

        new FirSimplu("Trei").start();    // “lansarea” celui de-al treilea thread

    }

 }

 

In laborator:

1. Inchideti proiectele anterioare (Ctrl+W). Creati un nou proiect fire

    (Project -> New Project…, selectati D:/, apoi Software2006, apoi numarul grupei, si scrieti fire).

2. Creati o noua clasa, numita FirSimplu, folosind codul dat mai sus.

3. Creati un obiect tip FirSimplu (cu nume "Test"), inspectati-i campurile (atributele) si metodele.

4. Tot in proiectul fire creati clasa DemoTreiFire folosind codul de mai sus. Compilati clasele.

5. Right-click pe clasa, selectati si executati main(). Urmariti efectul in Terminal Window.

 

Doua variante (aplicatie de sine statatoare  si  applet) de program "ceas" bazat pe threads:

 

import java.util.*;

import java.text.DateFormat;

import java.io.*;

 

public class CeasConsola implements Runnable {

 

  private Thread firCeas = null;

  private static int contor = 0;

 

 

  public void start() {                   // (re)pornire fir

    if (firCeas == null) {

      firCeas = new Thread(this);

      firCeas.start();                    // apeleaza run()

    }

    System.out.println("Ceas pornit");

  }

  public void run() {                    // executie fir

    Thread firulMeu = Thread.currentThread();

    contor++;

 

    while (firCeas == firulMeu) {

 

      Calendar cal = Calendar.getInstance();

      Date date = cal.getTime();

      DateFormat df = DateFormat.getTimeInstance();

      System.out.println("[" + contor + "]  " +

                   df.format(date));   

 

      try { Thread.sleep(1000); }

      catch (InterruptedException e){ }

    }

  }

 

  public void stop() {                            // oprire fir

    System.out.println("Ceas oprit");

    firCeas = null;                                // firul dispare

  }

  public static void main(String[] args)

                            throws IOException {

    CeasConsola ceas;

    BufferedReader in = new BufferedReader

          (new InputStreamReader(System.in));

    while (true) {

      ceas = new CeasConsola();

      ceas.start();        // (re)pornire fir la citirea

      in.readLine();       // unui caracter de la consola

      ceas.stop();        // oprire fir la citirea

      in.readLine();       // unui caracter de la consola

    }

  }

}

import java.applet.Applet;

import java.awt.*;

import java.util.*;

import java.text.DateFormat;

public class CeasApplet extends Applet

                                             implements Runnable {

  private Thread firCeas = null;

  private static int contor = 0;

  public void init() { setBackground(Color.white); }

 

  public void start() {                       // (re)pornire fir

    if (firCeas == null) {

      firCeas = new Thread(this, "Ceas");

      firCeas.start();                         // apeleaza run()

    }

  }

 

  public void run() {                        // executie fir

    Thread firulMeu = Thread.currentThread();

    contor++;

 

    while (firCeas == firulMeu) {

      repaint();         // apeleaza paint() (redeseneaza)

      try { Thread.sleep(1000); }

      catch (InterruptedException e){ }

    }

  }

  public void paint(Graphics g) {      // deseneaza

     Calendar cal = Calendar.getInstance();

     Date date = cal.getTime();

     DateFormat df = DateFormat.getTimeInstance();

     g.drawString("[" + contor + "]  " +

                        df.format(date), 5, 10);

  }

  public void stop(){                        // oprire fir

    firCeas = null;                           // firul dispare

  }

}

// Applet-ul nu are metoda principala.

// Metodele start(), stop(), run(), etc. sunt invocate

// de browserul Web in care este “executat”.

/*     Exemplu de includere intr-o pagina Web (HTML):

    <HTML>
         <TITLE>  
              Applet Ceas  
         </TITLE>
         <BODY> 
              <APPLET CODE=CeasApplet.class 
                     WIDTH=200 HEIGHT=50> 
              </APPLET> 
         </BODY>
    </HTML>     */

 

In laborator:

 

1. Testati applet-ul CeasApplet folosind link-ul dat.        Ceas

 

 

 

5.3. Interfete grafice cu utilizatorul (GUI)

 

5.3.1. Modalitati de a crea containerul de nivel maxim

 

 

Pentru a crea containere de nivel maxim (detalii privind interfetele grafice swing in Java) exista mai multe modalitati, printre care:

 

1. Utilizarea unui obiect de tip JFrame,

 

 

 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 public class TestUtilizareFrame_Swing  {      // Test grafic cu JtextField si JTextArea
   private static JTextField inTextGrafic;     // Intrare -linie de text grafica (JtextField)
   private static JTextArea outTextGrafic;     // Iesire - zona de text grafica (JtextArea)
 
   public TestUtilizareFrame_Swing() {         // Initializari grafice
      // Crearea obiectului cadru (frame), cu titlu specificat
      JFrame frame = new JFrame("Test utilizare Frame"); 
      Container containerCurent = frame.getContentPane();
      containerCurent.setLayout(new BorderLayout());
 
      outTextGrafic = new JTextArea(5, 40);    // Zona de text non-editabila de iesire 
      JScrollPane scrollPane = new JScrollPane(outTextGrafic,
                     JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, 
                     JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
 
      containerCurent.add("Center", scrollPane);
      outTextGrafic.setEditable(false);
 
      inTextGrafic = new JTextField(40);       // Camp de text editabil de intrare
      containerCurent.add("South", inTextGrafic);
 
      frame.pack();                 // Impachetarea (compactarea) componentelor 
      frame.setVisible(true);       // Fereastra devine vizibila 
      inTextGrafic.requestFocus();  // Cerere focus pe intrarea text din fereastra curenta     
   }
 
   public static void main (String args[]) {
      TestUtilizareFrame_Swing testUtilizareFrame = new TestUtilizareFrame_Swing();
   }
 }

 

 

 

 

In laborator:

1. Inchideti proiectele anterioare (Ctrl+W). Creati un nou proiect gui

    (Project -> New Project…, selectati D:/, apoi Software2006, apoi numarul grupei, si scrieti gui).

2. Creati o noua clasa, numita TestUtilizareFrame_Swing, folosind codul dat mai sus.

3. Compilati codul, apoi right-click pe clasa, selectati si executati main(). Urmariti efectul.

 

 

2. Extinderea prin mostenire a clasei JFrame,

 

 

 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 public class TestExtindereFrame_Swing extends JFrame { // Test cu JtextField si JTextArea
   private static JTextField inTextGrafic;     // Intrare -linie de text grafica (JtextField)
   private static JTextArea outTextGrafic;     // Iesire - zona de text grafica (JtextArea)
 
   public TestExtindereFrame_Swing() {         // Initializari grafice
      super("Test extindere Frame");           // Stabilire titlu fereastra (JFrame)
      Container containerCurent = this.getContentPane();
      containerCurent.setLayout(new BorderLayout());
 
      outTextGrafic = new JTextArea(5, 40);    // Zona de text non-editabila de iesire 
      JScrollPane scrollPane = new JScrollPane(outTextGrafic,
                     JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, 
                     JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
 
      containerCurent.add("Center", scrollPane);
      outTextGrafic.setEditable(false);
 
      inTextGrafic = new JTextField(40);       // Camp de text editabil de intrare
      containerCurent.add("South", inTextGrafic);
 
      pack();                      // Impachetarea (compactarea) componentelor in container
      setVisible(true);            // Fereastra devine vizibila 
      inTextGrafic.requestFocus(); // Cerere focus pe intrarea de text din fereastra curenta     
   }
   public static void main (String args[]) {
      TestExtindereFrame_Swing testFrame = new TestExtindereFrame_Swing();
   }
 }

 

 

 

 

In laborator:

1. Tot in proiectul gui creati o noua clasa, numita TestExtindereFrame_Swing, folosind codul dat.

2. Compilati codul, apoi right-click pe clasa, selectati si executati main(). Urmariti efectul.

 

 

 

 

 

5.3.2. Crearea interactivitatii in interfetele grafice

 

 

Pentru introducerea interactivitatii, (detalii privind interfetele grafice swing in Java) trebuie tratate evenimentele din interfata grafica. In Java exista mai multe moduri de tratare a evenimentelor.

 

 

Incepand cu versiunea initiala, JDK 1.0, interfetele grafice realizate cu biblioteca AWT au 2 moduri de tratare a evenimentelor:

 

 

1. Implementand metoda action(), care:

 

- primeste ca parametri un obiect de tip Event care incapsuleaza evenimentul produs, si un obiect de tip Object care incapsuleaza parametrii acestuia,

 

- testeaza atributele target si id ale obiectului de tip Event pentru a identifica obiectul tinta (in care s-a produs evenimentul) si tipul de actiune produsa, si trateaza apoi evenimentul respectiv

 

 

2. Implementand metoda handleEvent(), care:

 

- primeste ca parametru un obiect de tip Event care incapsuleaza evenimentul produs,

 

- testeaza atributele target si id ale obiectului de tip Event pentru a identifica obiectul tinta (in care s-a produs evenimentul) si tipul de actiune produsa, si trateaza apoi evenimentul respectiv

 

 

Incepand cu versiunea JDK 1.1, interfetele grafice realizate cu biblioteca AWT au:

 

3. Un nou mod de tratare a evenimentelor, utilizat si de interfetele grafice Swing, care presupune trei operatii:

 

 

I. (a) declararea unei clase care implementeaza o interfata « ascultator de evenimente », (care contine metode ce trebuie implementate de utilizator pentru tratarea evenimentului respectiv), sau (b) declararea unei clase care extinde o clasa predefinita care implementeaza o interfata « ascultator de evenimente »

 

 

 

II. (a) implementarea tuturor metodelor definite in interfata « ascultator de evenimente », sau (b) re-implementarea metodelor dorite din clasa care implementeaza interfata

 

 

 

 

III. inregistrarea unui obiect din clasa « ascultator de evenimente » de catre fiecare dintre componentele grafice (numite tinta sau sursa) pentru care se doreste tratarea evenimentului respectiv

 

Programul EcouGrafic_Swing exemplifica crearea unei ferestre principale prin extinderea clasei JFrame si tratarea evenimentelor.

 

 

 

 import java.awt.*;
 import java.awt.event.*;
 import javax.swing.*;
 public class EcouGrafic_Swing extends JFrame { // Ecou grafic cu JtextField si JTextArea
   private static JTextField inTextGrafic;      // Intrare - linie text grafica (JtextField)
   private static JTextArea outTextGrafic;      // Iesire - zona text grafica (JtextArea)
   private static JScrollBar vertical;
   public EcouGrafic_Swing() {                  // Initializari grafice
      super("Ecou grafic simplu Swing");        // Stabilire titlu fereastra (JFrame)
      Container containerCurent = this.getContentPane();
      containerCurent.setLayout(new BorderLayout());
 
      outTextGrafic = new JTextArea(5, 40);     // Zona text non-editabila de iesire 
      JScrollPane scrollPane = new JScrollPane(outTextGrafic,
                     JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, 
                     JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
      vertical = scrollPane.getVerticalScrollBar();
      containerCurent.add("Center", scrollPane);
      outTextGrafic.setEditable(false);
      outTextGrafic.append("Pentru oprire introduceti '.' si <Enter>\n\n");
 
      inTextGrafic = new JTextField(40);        // Camp de text editabil de intrare
      containerCurent.add("South", inTextGrafic);
 
      // Inregistrare obiect "ascultator" de "evenimente actionare" la "obiectul sursa"
      inTextGrafic.addActionListener(new AscultatorInText());
      // Inregistrare obiect "ascultator" de "evenimente fereastra" la "sursa" fereastra
      this.addWindowListener(new AscultatorInchidere());
 
      pack();                      // Impachetarea (compactarea) componentelor in container
      setVisible(true);            // Fereastra devine vizibila 
      inTextGrafic.requestFocus(); // Cerere focus pe intrarea text din fereastra curenta     
   }
   // Clasa interna "ascultator" de "evenimente actionare" 
   // implementeaza interfata ActionListener
   class AscultatorInText implements ActionListener {
      // Tratarea actionarii intrarii de text (introducerii unui "Enter")
      public void actionPerformed(ActionEvent ev) { 
         String sirCitit = inTextGrafic.getText();  // Citirea unei linii din intrarea text                  
         inTextGrafic.setText("");                  // Golirea intrarii text 
         outTextGrafic.append("S-a introdus: "+sirCitit+"\n"); // Scriere linie in zona text 
         vertical.setValue(vertical.getMaximum() - vertical.getVisibleAmount());
         validate(); repaint();
         if (sirCitit.equals(new String(".")))   System.exit(0);   // Conditie terminare 
      }      
   }
   public static void main (String args[]) {
      EcouGrafic_Swing ecouGrafic = new EcouGrafic_Swing();
   }
 }
 // Clasa "adaptor pentru ascultator" de "evenimente fereastra" extinde WindowAdapter
 class AscultatorInchidere extends WindowAdapter {
    // Tratarea inchiderii ferestrei curente
    public void windowClosing(WindowEvent ev) { System.exit(0); } // Terminarea programului }
 }

 

 

In laborator:

1. Tot in proiectul gui creati o noua clasa, numita EcouGrafic_Swing, folosind codul dat.

2. Compilati codul, apoi right-click pe clasa, selectati si executati main(). Urmariti efectul.

 

 

 

5.4. Precizari privind « colocviul » la laborator

 

La ultima lucrare de laborator (urmatoarea, a 6-a, in ianuarie 2007)  vor fi aduse:

- Temele de la lucrarea de laborator 1:

* Sinteza (interpretarea) raporturilor de eroare obtinute prin modificarea programului Salut.java (scrisa pe hartie, nu tiparita la imprimanta).

* Codurile sursa ale programelor Polinom0, Polinom1 si Polinom2 completate.

- Temele de la lucrarea de laborator 2:

* Listingul (sau textul scris de mana) cu sarcinile indeplinite pentru a obtine desenul casei

* Codurile Java pentru cele 2 clase care corespund numarului de ordine al fiecarui student, codurile continand cel putin 3 campuri/atribute si 3 metode/operatii.

- Temele de la lucrarea de laborator 3:

* Codurile finale ale claselor  DialogUtilizator02Mesaj02 si  Pachet02 (listinguri).

* Codurile celor 2 clase date ca tema data trecuta (listinguri), fiecare continand: cel putin 3 campuri/atribute, cel putin 2 constructori, (cerinta noua!) cel putin 3 metode/operatii. Codurile metodelor si constructorilor vor neaparat fi complete!

- Temele de la lucrarea de laborator 4:

* Codurile finale ale claselor ReleuDerivat, ClientBidirectional, ServerBidirectional, si ReleuBidirectional  (scrise pe hartie sau listinguri!)

 

Pe baza acestor teme (date la lucrarile 1-4), in urma unor discutii individuale, si tinand cont de activitatea la laborator, vor fi stabilite calificativele la laborator.