[WikiDyd] [TitleIndex] [WordIndex

Języki i metodyka programowania 2

Wykłady z wiosny 2011: W1 (21 lutego) - W4 (14 marca)

W1: 21 lutego 2011

Mówiliśmy (jeszcze raz ;-)) o projektowaniu programu i podziale go na moduły. Na przykładach pokazaliśmy, że pisane dotychczas programy składały się zazwyczaj z kilku typowych modułów (których szczegóły różnią się w różnych projektach, ale podstawowa funkcjonalność jest podobna):

Powiedzieliśmy, że podział programu na moduły:

Przypomnieliśmy, że podstawowe narzędzia służące do zarządzania kodem to programy typu make i systemy kontroli wersji.

W2: 28 lutego 2011

Rozpoczynamy Javę. Powiedzieliśmy co-nieco o historii, znaczeniu i właściwościach języka. Napisaliśmy pierwsze klasy.

Stos.java:

   1 public class Stos {
   2 
   3     public Stos( ) {
   4         t= new double[4];
   5         n= 0;
   6     }
   7 
   8     public void push( double x ) {
   9         if( n >= t.length )
  10             doubleSize();
  11         t[n++]= x;
  12     }
  13 
  14     public double pop() {
  15         return t[--n];
  16     }
  17 
  18     public boolean isEmpty() {
  19         return n == 0;
  20     }
  21 
  22     private void doubleSize() {
  23         double [] nt= new double[ t.length*2 ];
  24         for( int i= 0; i < n; i++ )
  25             nt[i]= t[i];
  26         t= nt;
  27     }
  28 
  29     private double [] t;
  30     private int n;
  31 }

TestStos.java

   1 public class TestStos {
   2     public static void main( String [] args ) {
   3         Stos s = new Stos();
   4 
   5         for( int i= 0; i < args.length; i++ )
   6             s.push( Double.parseDouble( args[i] ) );
   7 
   8         while( ! s.isEmpty() )
   9             System.out.println( s.pop() );
  10     }
  11 }

W3: 7 marca 2011

Przyglądamy się konstrukcji klasy. Omawiamy rolę konstruktora, metody statyczne, przeciążanie metod, podstawy dziedziczenia (przesłanianie metod i polimorfizm). Mówimy też o znaczeniu this, funkcjonowaniu kwalifikatora private i pojęciu klasy jako modułu programistycznego.

Zmieniona klasa stos: przeciążony konstruktor, licznik stosów, metoda statyczna udostępniająca licznik, metoda kopiująca i konstruktor kopiujący przesłonięcie metod toString.

   1 public class Stos {
   2 
   3     public Stos( ) {
   4         t= new double[4];
   5         n= 0;
   6         ileStosow++;
   7     }
   8 
   9     public Stos( int size ) {
  10         t = new double[ size ];
  11         n= 0;
  12         ileStosow++;
  13     }
  14 
  15     public Stos( Stos s ) {
  16         this.t= new double[ s.t.length ];
  17         this.n= s.n;
  18         for( int i= 0; i < n; i++ )
  19             this.t[i]= s.t[i];
  20         ileStosow++;
  21     }
  22 
  23     public Stos copy( ) {
  24         Stos ret= new Stos( this.t.length );
  25         ret.n= this.n;
  26         for( int i= 0; i < this.n; i++ )
  27             ret.t[i]= this.t[i];
  28         return ret;
  29     }
  30 
  31     public void push( double x ) {
  32         if( n >= t.length )
  33             doubleSize();
  34         t[n++]= x;
  35     }
  36 
  37     public double pop() {
  38         return t[--n];
  39     }
  40 
  41     public boolean isEmpty() {
  42         return n == 0;
  43     }
  44 
  45     public int size() {
  46         return n;
  47     }
  48 
  49     public String toString() {
  50         String ret= super.toString() + " { ";
  51         for( int i= 0; i < n; i++ )
  52             ret += t[i] + " ";
  53         for( int i= n; i < t.length; i++ )
  54             ret += "[] ";
  55         return ret + "}";
  56     }
  57 
  58     public static int count() {
  59         return ileStosow;
  60     }
  61 
  62     private void doubleSize() {
  63         double [] nt= new double[ t.length*2 ];
  64         for( int i= 0; i < n; i++ )
  65             nt[i]= t[i];
  66         t= nt;
  67     }
  68 
  69     private double [] t;
  70     private int n;
  71     private static int ileStosow;
  72 }

Poprawiona klasa testująca:

   1 public class TestStos {
   2     public static void main( String [] args ) {
   3         Stos s = new Stos();
   4         Stos s100 = new Stos(100);
   5         Stos c= s.copy();
   6 
   7         for( int i= 0; i < args.length; i++ ) {
   8             s.push( Double.parseDouble( args[i] ) );
   9             s100.push( Double.parseDouble( args[i] ) );
  10         }
  11 
  12         c= new Stos(s);
  13 
  14         System.out.println( "Stos c "
  15             + ( c.isEmpty() ? "jest" : "nie jest")
  16             + " pusty." );
  17 
  18         System.out.println( "Mamy " + s.count() + " stosow." );
  19 
  20         while( s.size() > 0 )
  21             System.out.println( s.pop() );
  22 
  23         System.out.println( "Stos s "
  24             + ( s.isEmpty() ? "jest" : "nie jest")
  25             + " pusty." );
  26 
  27         System.out.println( "Stos c "
  28             + ( c.isEmpty() ? "jest" : "nie jest")
  29             + " pusty." );
  30     }
  31 }

I druga klasa testująca pokazująca polimorfizm - zmienna c.

   1 public class TestStos2 {
   2     public static void main( String [] args ) {
   3         Stos s = new Stos();
   4         Object c= s.copy();
   5 
   6         for( int i= 0; i < args.length; i++ ) {
   7             s.push( Double.parseDouble( args[i] ) );
   8         }
   9 
  10         c= new Stos(s);
  11 
  12         System.out.println( "Stos c "
  13             + ( ((Stos)c).isEmpty() ? "jest" : "nie jest")
  14             + " pusty." );
  15 
  16         System.out.println( "Mamy " + s.count() + " stosow." );
  17 
  18         while( s.size() > 0 )
  19             System.out.println( s.pop() );
  20 
  21         System.out.println( "Stos s "
  22             + ( s.isEmpty() ? "jest" : "nie jest")
  23             + " pusty." );
  24 
  25         System.out.println( "Stos c "
  26             + ( ((Stos)c).isEmpty() ? "jest" : "nie jest")
  27             + " pusty." );
  28 
  29         if( ! ((Stos)c).isEmpty() ) {
  30             System.out.println( "Stos c: " + c );
  31         }
  32 
  33     }
  34 }

Wykład 4: 14 marca 2011

Omawialiśmy inicjalizację i niszczenie obiektów. Wyjaśniliśmy: statyczny blok inicjujący, zwykły blok inicjujący, konstruktor, metodę finalize.

Przykładowa klasa:

   1 public class Gracz {
   2     private Gracz( String nazwa, double punkty) {
   3         System.err.println( "Jestem w konstruktorze 2-argumentowym, robie " + nazwa );
   4         this.nazwa= nazwa;
   5         this.punkty= punkty;
   6         System.err.println( "Wychodze z konstr. 2-arg" );
   7     }
   8 
   9     private Gracz( String nazwa ) {
  10         System.out.println( "Jestem w konstruktorze 1-argumentowym, robie " + nazwa );
  11         this.nazwa= nazwa;
  12         double sr= 0;
  13         //System.err.println( "liczbaGraczy=" + liczbaGraczy );
  14         for( int i= 0; i < liczbaGraczy; i++ ) {
  15             //System.err.println( "i=" + i + " gracze[i]=" + gracze[i] );
  16             sr+= gracze[i].punkty;
  17         }
  18         this.punkty= sr / liczbaGraczy;
  19         System.err.println( "Wychodze z konstr. 1-arg" );
  20     }
  21 
  22     public String toString() {
  23         return "(" + nazwa + " " + punkty + ")";
  24     }
  25 
  26     public void finalize() {
  27         System.err.println( nazwa + ": do widzenia!" );
  28     }
  29 
  30     public static Gracz nowyGracz( String nazwa ) {
  31         System.err.println( "Jestem w nowyGracz" );
  32         if( liczbaGraczy >= gracze.length )
  33             return null;
  34         else {
  35             gracze[liczbaGraczy]= new Gracz( nazwa );
  36             liczbaGraczy++;
  37             return gracze[liczbaGraczy-1];
  38         }
  39     }
  40 
  41     public static void usunGracz( Gracz x ) {
  42         for( int i= 0; i < liczbaGraczy; i++ )
  43             if( gracze[i] == x ) {
  44                 System.err.println( "Usuwam gracza " + x );
  45                 gracze[i] = gracze[liczbaGraczy-1];
  46                 //gracze[liczbaGraczy-1]= null; // to jest rozwiązanie zadania domowego
  47                 liczbaGraczy--;
  48             }
  49     }
  50 
  51     public static void listuj() {
  52         for( int i= 0; i < liczbaGraczy; i++ )
  53             System.err.println( gracze[i] );
  54     }
  55 
  56     private String nazwa;
  57     private double punkty;
  58     private static Gracz [] gracze;
  59     private static int liczbaGraczy;
  60 
  61     static {
  62         System.err.println( "Jestem w statycznym bloku inicjujoncym" );
  63         gracze= new Gracz[10];
  64         gracze[0] = new Gracz( "System", 100 );
  65         gracze[1] = new Gracz( "Kasjer", 1000 );
  66         liczbaGraczy= 2;
  67         System.err.println( "Wychodze ze statycznego bloku inic." );
  68     }
  69 
  70     {
  71         System.err.println( "W zwyklym BI" );
  72     }
  73 }

I klasa testująca:

   1 public class TestGracz {
   2     public static void main( String [] args ) {
   3         Gracz [] gra= new Gracz[100];
   4         int n= 0;
   5 
   6         Gracz.listuj();
   7 
   8         System.err.println( "Przed forEach" );
   9         for( String s : args )
  10             gra[n++]= Gracz.nowyGracz( s );
  11 
  12         for( int i= 0; i < n; i++ )
  13             System.out.println( gra[i] );
  14 
  15         Gracz.listuj();
  16 
  17         for( int i= 0; i < n; i++ ) {
  18             Gracz.usunGracz( gra[i] );
  19             gra[i] = null;
  20         }
  21 
  22         Gracz.listuj();
  23 
  24         System.gc();
  25 
  26         System.err.println( "Po wywolaniu gc" );
  27     }
  28 }

Zadanie domowe polegało na wykryciu, dlaczego po uruchomieniu odśmiecacza nie widać wydruków generowanych przez metody finalize wszystkich obiektów:

lap-jstar:~/jimp/2> java TestGracz ja ty on
Jestem w statycznym bloku inicjujoncym
W zwyklym BI
Jestem w konstruktorze 2-argumentowym, robie System
Wychodze z konstr. 2-arg
W zwyklym BI
Jestem w konstruktorze 2-argumentowym, robie Kasjer
Wychodze z konstr. 2-arg
Wychodze ze statycznego bloku inic.
(System 100.0)
(Kasjer 1000.0)
Przed forEach
Jestem w nowyGracz
W zwyklym BI
Jestem w konstruktorze 1-argumentowym, robie ja
Wychodze z konstr. 1-arg
Jestem w nowyGracz
W zwyklym BI
Jestem w konstruktorze 1-argumentowym, robie ty
Wychodze z konstr. 1-arg
Jestem w nowyGracz
W zwyklym BI
Jestem w konstruktorze 1-argumentowym, robie on
Wychodze z konstr. 1-arg
(ja 550.0)
(ty 550.0)
(on 550.0)
(System 100.0)
(Kasjer 1000.0)
(ja 550.0)
(ty 550.0)
(on 550.0)
Usuwam gracza (ja 550.0)
Usuwam gracza (ty 550.0)
Usuwam gracza (on 550.0)
(System 100.0)
(Kasjer 1000.0)
Po wywolaniu gc
ja: do widzenia!
lap-jstar:~/jimp/2>

Kolejnym problemem, który ma nam pokazać znaczenie dziedziczenia i polimorfizmu jest zadanie napisania kalkulatora, który będzie wykonywał proste działania arytmetyczne zapisane w odwrotnej notacji polskiej, ale umożliwiał obliczenia na liczbach binarnych, rzymskich itp. Pożądaną funkcjonalność można zilustrować tak:

$java 2 2 + =
4
$java Binary 100 10 \* =
1000
$java Roman XLV XIV / II + =
V

W pierwszym podejściu napisaliśmy czterodziałaniowy kalkulator ONP dla ,,zwykłych" liczb:

   1 public class ONP {
   2     public static void main( String [] args ) {
   3         Stos s= new Stos(); // taka klasa powstała na Wykladzie 3
   4         Double x;
   5         for( String a : args ) {
   6             try {
   7                 x = Double.parseDouble( a );
   8                 s.push( x );
   9             } catch( NumberFormatException e ) {
  10                 if( a.equals( "+" ) ) {
  11                     x = s.pop();
  12                     x.add( s.pop() );
  13                     s.push( x );
  14                 } else if( a.equals( "*" ) )
  15                     s.push( s.pop() * s.pop() );
  16                 else if( a.equals( "-" ) ) {
  17                     x= s.pop();
  18                     s.push( s.pop() - x );
  19                 } else if( a.equals( "/" ) ) {
  20                     x= s.pop();
  21                     s.push( s.pop() / x );
  22                 } else if( a.equals( "=" ) ) {
  23                     System.out.println( "=" + s.pop() );
  24                 } else 
  25                     System.err.println( "Nieznany symbol: " + a );
  26             }
  27         }
  28     }
  29 }

Sprawdziliśmy, że działa:

lap-jstar:~/jimp/2> java ONP 2 2 + 3 \* =
=12.0
lap-jstar:~/jimp/2> java ONP 5 0 / =       
=Infinity
lap-jstar:~/jimp/2>

Myśląc o implementacji kalkulatora obsługującego ,,dowolne" liczby, rozpoczęliśmy od napisania stosu, który może przechowywać dowolne obiekty:

   1 public class XStos {
   2 
   3     public XStos( ) {
   4         t= new Object[4];
   5         n= 0;
   6     }
   7 
   8     public XStos( int size ) {
   9         t = new Object[ size ];
  10         n= 0;
  11     }
  12 
  13     public XStos( XStos s ) {
  14         this.t= new Object[ s.t.length ];
  15         this.n= s.n;
  16         for( int i= 0; i < n; i++ )
  17             this.t[i]= s.t[i];
  18     }
  19 
  20     public XStos copy( ) {
  21         XStos ret= new XStos( this.t.length );
  22         ret.n= this.n;
  23         for( int i= 0; i < this.n; i++ )
  24             ret.t[i]= this.t[i];
  25         return ret;
  26     }
  27 
  28     public void push( Object x ) {
  29         if( n >= t.length )
  30             doubleSize();
  31         t[n++]= x;
  32     }
  33 
  34     public Object pop() {
  35         return t[--n];
  36     }
  37 
  38     public boolean isEmpty() {
  39         return n == 0;
  40     }
  41 
  42     public int size() {
  43         return n;
  44     }
  45 
  46     public String toString() {
  47         String ret= super.toString() + " { ";
  48         for( int i= 0; i < n; i++ )
  49             ret += t[i] + " ";
  50         for( int i= n; i < t.length; i++ )
  51             ret += "[] ";
  52         return ret + "}";
  53     }
  54 
  55     private void doubleSize() {
  56         Object [] nt= new Object[ t.length*2 ];
  57         for( int i= 0; i < n; i++ )
  58             nt[i]= t[i];
  59         t= nt;
  60     }
  61 
  62     public static void main( String [] args ) {
  63         XStos xs= new XStos();
  64         for( String a : args )
  65             xs.push( a );
  66 
  67         for( String a: args ) {
  68             try {
  69                 double x = Double.parseDouble( a );
  70                 xs.push( x );
  71             } catch( NumberFormatException e ) {
  72                 xs.push( e );
  73             }
  74         }
  75 
  76 
  77         System.out.println( xs );
  78     }
  79 
  80     private Object [] t;
  81     private int n;
  82 }

Sprawdziliśmy, że istotnie można tam włożyć wszystko: napisy (String), liczby (double automatycznie opakowywane przez Double), a nawet wyjątki (NumberFormatException):

lap-jstar:~/jimp/2> java XStos 1 ala ma 2 koty
XStos@119298d { 1 ala ma 2 koty 1.0 java.lang.NumberFormatException: For input string: "ala" java.lang.NumberFormatException: For input string: "ma" 2.0 java.lang.NumberFormatException: For input string: "koty" [] [] [] [] [] [] }
lap-jstar:~/jimp/2> 

Na następnych zajęciach napiszemy klasę Liczba, która posłuży nam do wykonywania działań. Kolejnym krokiem będzie rozszerzenie klasy Liczba na typy Binary i Roman.


2015-09-23 06:44