Skip to content

2. Podstawy języka Java

2.1. Wprowadzenie

Na początku traktujemy Javę jako klasyczny język programowania. Kwestie związane z obiektami omówimy w dalszej części.

W programie występują dwa rodzaje elementów

  • dane
  • instrukcje, które nimi operują

Zazwyczaj staramy się oddzielić dane od instrukcji:

Image

2.2. Dane w Javie

W języku Java stosuje się następujące typy danych:

  • liczby całkowite
  • liczby rzeczywiste
  • znaki i ciągi znaków
  • wartości logiczne
  • obiekty

2.2.1. Wstępnie zdefiniowane typy danych

Typ
Kodowanie
Domeny
char
2 bajty
znak Unicode
int
4 bajty
[-231, 231-1]
long
8 bajtów
[-263, 263 -1]
byte
1 bajt
[-27 , 27 -1]
short
2 bajty
[-215, 215-1]
float
4 bajty
[3.4 10-38, 3.4 10+38] w wartościach bezwzględnych
double
8 bajtów
[1.7 10-308 , 1.7 10+308] w wartościach bezwzględnych
boolean
1 bit
prawda, fałsz
String
odwołanie do obiektu
ciąg znaków
Date
odwołanie do obiektu
data
Character
numer identyfikacyjny obiektu
znak
Integer
odniesienie do obiektu
int
Long
odwołanie do obiektu
long
Byte
identyfikator obiektu
bajt
Float
odwołanie do obiektu
float
Double
odwołanie do obiektu
double
Boolean
odwołanie do obiektu
boolean

2.2.2. Notacja danych literałowych

entier
145, -7, 0xFF (szesnastkowo)
liczba rzeczywista typu double
134,789, -45E-18 (-45 × 10⁻¹⁸)
liczba zmiennoprzecinkowa typu float
134.789F, -45E-18F (-45 × 10⁻¹⁸)
caractère
„A”, „b”
ciąg znaków
„dzisiaj”
booléen
prawda, fałsz
date
new Date(13,10,1954) (dzień, miesiąc, rok)

2.2.3. Zgłaszanie danych

2.2.3.1. Rola deklaracji

Program operuje na danych charakteryzujących się nazwą i typem. Dane te są przechowywane w pamięci. Podczas kompilacji programu kompilator przypisuje każdej danym miejsce w pamięci określone adresem i rozmiarem. Czyni to na podstawie deklaracji wprowadzonych przez programistę.

Ponadto deklaracje te pozwalają kompilatorowi wykrywać błędy programistyczne. Na przykład operacja

x=x*2;

zostanie uznana za błędną, jeśli x jest na przykład ciągiem znaków.

2.2.3.2. Deklaracja stałych

Składnia deklaracji stałej jest następująca:

final typ nazwa=wartość; //definiuje stałą nazwa=wartość

np.: final float PI=3.141592F;

Uwaga

Po co deklarować stałe?

  • Program będzie łatwiejszy do odczytania, jeśli nadamy stałej znaczącą nazwę:

np.: final float taux_tva = 0.186F;

  • Modyfikacja programu będzie łatwiejsza, jeśli „stała” ulegnie zmianie. Tak więc w powyższym przypadku, jeśli stawka VAT wzrośnie do 33%, jedyną zmianą, jaką trzeba będzie wprowadzić, będzie zmiana instrukcji definiującej jej wartość:

final float taux_tva=0.33F;

Gdyby w programie użyto bezpośrednio wartości 0,186, konieczna byłaby zmiana wielu instrukcji.

2.2.3.3. Deklaracja zmiennych

Zmienna jest identyfikowana przez nazwę i odnosi się do typu danych. Nazwa zmiennej w Javie składa się z n znaków, z których pierwszy musi być literą, a pozostałe mogą być literami lub cyframi. Java rozróżnia wielkie i małe litery. W związku z tym zmienne FIN i fin są różne.

Zmienne można zainicjować podczas ich deklaracji. Składnia deklaracji jednej lub kilku zmiennych jest następująca:

identificateur_de_type variable1,variable2,...,variablen;

gdzie identificateur_de_type jest typem predefiniowanym lub typem obiektowym zdefiniowanym przez programistę.

2.2.4. Konwersje między liczbami a ciągami znaków

liczba -> ciąg znaków
"" + liczba
ciąg znaków -> int
Integer.parseInt(ciąg znaków)
ciąg znaków -> long
Long.parseLong(ciąg)
ciąg znaków -> double
Double.valueOf(ciąg).doubleValue()
ciąg znaków -> liczba zmiennoprzecinkowa
Float.valueOf(ciąg).floatValue()

Oto program przedstawiający główne techniki konwersji między liczbami a ciągami znaków. Konwersja ciągu znaków na liczbę może się nie powieść, jeśli ciąg ten nie reprezentuje prawidłowej liczby. W takim przypadku generowany jest błąd krytyczny, zwany w Javie wyjątkiem. Błąd ten można obsłużyć za pomocą następującej klauzuli try/catch:

try{
    appel de la fonction susceptible de générer l'exception
} catch (Exception e){
    traiter l'exception e
}
instruction suivante

Jeśli funkcja nie generuje wyjątku, przechodzi się do następnej instrukcji; w przeciwnym razie przechodzi się do treści klauzuli catch, a następnie do następnej instrukcji. Do kwestii obsługi wyjątków powrócimy w późniejszym terminie.


import java.io.*;

public class conv1{

  public static void main(String arg[]){
    String S;
    final int i=10;
    final long l=100000;
    final float f=(float)45.78;
    double d=-14.98;

    // liczba --> ciąg znaków
    S=""+i;
    affiche(S);
    S=""+l;
    affiche(S);
    S=""+f;
    affiche(S);
    S=""+d;
    affiche(S);

    //wartość logiczna --> ciąg znaków
    final boolean b=false;
    S=""+new Boolean(b);
    affiche(S);

    // ciąg znaków --> int
    int i1;
    i1=Integer.parseInt("10");
    affiche(""+i1);
    try{
      i1=Integer.parseInt("10.67");
      affiche(""+i1);
    } catch (Exception e){
      affiche("Erreur "+e);
    }

    // ciąg znaków --> long
    long l1;
    l1=Long.parseLong("100");
    affiche(""+l1);
    try{
      l1=Long.parseLong("10.675");
      affiche(""+l1);
    } catch (Exception e){
      affiche("Erreur "+e);
    }
    
    // ciąg znaków --> double
    double d1;
    d1=Double.valueOf("100.87").doubleValue();
    affiche(""+d1);
    try{
      d1=Double.valueOf("abcd").doubleValue();
      affiche(""+d1);
    } catch (Exception e){
      affiche("Erreur "+e);
    }

    // ciąg znaków --> zmiennoprzecinkowa
    float f1;
    f1=Float.valueOf("100.87").floatValue();
    affiche(""+f1);
    try{
      d1=Float.valueOf("abcd").floatValue();
      affiche(""+f1);
    } catch (Exception e){
      affiche("Erreur "+e);
    }

}// koniec main

  public static void affiche(String S){
    System.out.println("S="+S);
  }
}// koniec klasy

Otrzymane wyniki są następujące:

S=10
S=100000
S=45.78
S=-14.98
S=false
S=10
S=Erreur java.lang.NumberFormatException: 10.67
S=100
S=Erreur java.lang.NumberFormatException: 10.675
S=100.87
S=Erreur java.lang.NumberFormatException: abcd
S=100.87
S=Erreur java.lang.NumberFormatException: abcd

2.2.5. Tablice danych

Tablica w Javie to obiekt umożliwiający zgromadzenie danych tego samego typu pod jednym identyfikatorem. Jej deklaracja wygląda następująco:

Typ Tablica[] = new Typ[n] lub Typ[] Tablica = new Typ[n]

Obie składnie są poprawne. n to liczba elementów, które może zawierać tablica. Składnia Tablica[i] odnosi się do elementu o numerze i, gdzie i należy do przedziału [0,n-1]. Każde odwołanie do elementu Tabela[i], w którym i nie należy do przedziału [0,n-1], spowoduje wygenerowanie wyjątku.

Tablicę dwuwymiarową można zadeklarować w następujący sposób:

Type Tablica[][] = new Type[n][p] lub Type[][] Tablica = new Type[n][p]

Składnia Tablica[i] oznacza element o numerze i z tablicy Tableau, gdzie i należy do przedziału [0,n-1]. Tabela[i] sama w sobie jest tabelą: Tabela[i][j] oznacza dany element nr j z Tabeli[i], gdzie j należy do przedziału [0,p-1]. Każde odwołanie do danych z Tableau przy użyciu nieprawidłowych indeksów powoduje błąd krytyczny.

Oto przykład:


public class test1{

  public static void main(String arg[]){
    float[][] taux=new float[2][2];
    taux[1][0]=0.24F;
    taux[1][1]=0.33F;
    System.out.println(taux[1].length);
    System.out.println(taux[1][1]);
  }
}

oraz wyniki wykonania:

2
0.33

Tablica jest obiektem posiadającym atrybut length: jest to rozmiar tablicy.

2.3. Podstawowe instrukcje języka Java

Rozróżnia się

  • instrukcje podstawowe wykonywane przez komputer
  • instrukcje sterujące przebiegiem programu.

Instrukcje elementarne stają się jasne, gdy przyjrzymy się strukturze mikrokomputera i jego urządzeń peryferyjnych.

Image

  1. odczyt danych z klawiatury

  2. przetwarzanie informacji

  3. wyświetlanie informacji na ekranie

  4. odczyt informacji z pliku na dysku

  5. zapisywanie informacji do pliku na dysku

2.3.1. Zapis na ekranie

Składnia instrukcji zapisu na ekranie jest następująca:

System.out.println(wyrażenie) lub System.err.println(wyrażenie)

gdzie expression to dowolny typ danych, który można przekonwertować na ciąg znaków w celu wyświetlenia na ekranie. W poprzednim przykładzie widzieliśmy dwie instrukcje zapisu:

System.out.println(taux[1].length);
System.out.println(taux[1][1]);

System.out zapisuje do pliku tekstowego, którym domyślnie jest ekran. To samo dotyczy System.err. Pliki te mają numery (lub descripteur) odpowiednio 1 i 2. Strumień danych wejściowych z klawiatury (System.in) jest również traktowany jako plik tekstowy o deskryptorze 0. Systemy DOS i Unix obsługują przekazywanie danych między procesami za pomocą potoków (pipe):

    commande1 | commande2

Wszystko, co commande1 zapisuje za pomocą System.out, jest przekierowywane do wejścia System.in programu commande2. Innymi słowy, commande2 odczytuje za pomocą System.in dane wygenerowane przez commande2 za pomocą System.out, które w związku z tym nie są już wyświetlane na ekranie. System ten jest szeroko stosowany w systemie Unix. W tej sekwencji strumień System.err nie jest przekierowywany: zapisuje on dane na ekranie. Dlatego właśnie służy on do wyświetlania komunikatów o błędach (stąd jego nazwa err): mamy pewność, że podczas przekierowywania poleceń komunikaty o błędach będą nadal wyświetlane na ekranie. Należy więc przyzwyczaić się do wyświetlania komunikatów o błędach na ekranie za pomocą strumienia System.err, a nie strumienia System.out.

2.3.2. Odczyt danych wprowadzonych z klawiatury

Strumień danych pochodzących z klawiatury jest oznaczony obiektem System.in typu InputStream. Ten typ obiektów umożliwia odczytywanie danych znak po znaku. Zadaniem programisty jest następnie odnalezienie w tym strumieniu znaków informacji, które go interesują. Typ InputStream nie pozwala na odczytanie całej linii tekstu za jednym razem. Typ BufferedReader umożliwia to za pomocą metody readLine.

Aby móc odczytywać wiersze tekstu wpisane z klawiatury, z strumienia wejściowego System.in typu InputStream tworzy się kolejny strumień wejściowy, tym razem typu BufferedReader:

BufferedReader IN=new BufferedReader(new InputStreamReader(System.in));

Nie będziemy tutaj wyjaśniać szczegółów tej instrukcji, która odwołuje się do pojęcia tworzenia obiektów. Wykorzystamy ją taką, jaka jest.

Tworzenie strumienia może zakończyć się niepowodzeniem: w takim przypadku generowany jest błąd krytyczny, zwany w Javie wyjątkiem. Za każdym razem, gdy metoda może wygenerować wyjątek, kompilator Javy wymaga, aby programista go obsłużył. Dlatego też, aby utworzyć powyższy strumień wejściowy, w rzeczywistości należy napisać:


BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
        System.err.println("Erreur " +e);
        System.exit(1);
}

Ponownie nie będziemy tutaj wyjaśniać obsługi wyjątków. Po utworzeniu poprzedniego strumienia IN można odczytać wiersz tekstu za pomocą instrukcji:

    String ligne;
    ligne=IN.readLine();

Wiersz wpisany z klawiatury jest zapisywany w zmiennej ligne i może być następnie wykorzystany przez program.

2.3.3. Przykład operacji wejścia-wyjścia

Oto program ilustrujący operacje wejścia i wyjścia z klawiatury i ekranu:


import java.io.*;        // wymagane do korzystania ze strumieni wejścia/wyjścia

public class io1{
  
  
  public static void main (String[] arg){
    
    // zapis do strumienia System.out
    Object obj=new Object();
    System.out.println(""+obj);
    System.out.println(obj.getClass().getName());
    
    // zapis do strumienia System.err
    int i=10;
    System.err.println("i="+i);
    
    // odczyt linii wprowadzonej z klawiatury
    String ligne;
    BufferedReader IN=null;
    try{
      IN=new BufferedReader(new InputStreamReader(System.in));
    } catch (Exception e){
      affiche(e);
      System.exit(1);
    }
    System.out.print("Tapez une ligne : ");
    try{
      ligne=IN.readLine();
      System.out.println("ligne="+ligne);
    } catch (Exception e){
      affiche(e);
      System.exit(2);
    }
  }//koniec wprowadzania danych

  public static void affiche(Exception e){
    System.err.println("Erreur : "+e);
  }

}//koniec klasy

oraz wyniki wykonania:

C:\Serge\java\bases\iostream>java io1
java.lang.Object@1ee78b
java.lang.Object
i=10
Tapez une ligne : je suis là
ligne=je suis là

Instrukcje

Object obj=new Object();
System.out.println(""+obj);
System.out.println(obj.getClass().getName());

mają na celu pokazanie, że dowolny obiekt może być wyświetlony. Nie będziemy tutaj wyjaśniać znaczenia tego, co jest wyświetlane. W bloku tym mamy również wyświetlenie wartości obiektu:

try{
      IN=new BufferedReader(new InputStreamReader(System.in));
    } catch (Exception e){
      affiche(e);
      System.exit(1);
    }

Zmienna e jest obiektem typu Exception, który wyświetlany jest tutaj za pomocą wywołania affiche(e). Spotkaliśmy się już, choć o tym nie wspominaliśmy, z takim wyświetleniem wartości wyjątku w programie konwersji, który widzieliśmy powyżej.

2.3.4. Przypisanie wartości wyrażenia do zmiennej

W tym miejscu skupiamy się na operacji variable=expression;

Wyrażenie może być typu: arytmetycznego, relacyjnego, boolowskiego, znakowego

2.3.4.1. Interpretacja operacji przypisania

Operacja variable=expression; sama w sobie jest wyrażeniem, którego obliczanie przebiega w następujący sposób:

  • Obliczana jest prawa strona operacji przypisania: wynikiem jest wartość V.
  • wartość V jest przypisywana do zmiennej
  • wartość V jest również wartością operacji przypisania, traktowaną tym razem jako wyrażenie.

W ten sposób operacja V1=V2=wyrażenie jest poprawna. Ze względu na kolejność operacji najpierw zostanie obliczony operator = znajdujący się najbardziej po prawej stronie. Otrzymujemy zatem V1=(V2=wyrażenie). Wyrażenie V2=wyrażenie jest obliczane i ma wartość V. Obliczenie tego wyrażenia spowodowało przypisanie wartości V do V2. Następny operator = jest zatem obliczany w postaci V1=V. Wartością tego wyrażenia jest nadal V. Jego obliczenie powoduje przypisanie wartości V do V1. Zatem operacja V1=V2=wyrażenie jest wyrażeniem, którego obliczenie

1: powoduje przypisanie wartości expression do zmiennych V1 i V2

2: zwraca jako wynik wartość expression.

Można to uogólnić na wyrażenie typu: V1=V2=....=Vn=wyrażenie

2.3.4.2. Wyrażenie arytmetyczne

Operatory wyrażeń arytmetycznych są następujące:

+: dodawanie

  • : odejmowanie

*: mnożenie

/: dzielenie: wynikiem jest iloraz dokładny, jeśli przynajmniej jeden z operandów jest liczbą rzeczywistą. Jeśli oba operandy są liczbami całkowitymi, wynikiem jest iloraz całkowity. Tak więc 5/2 -> 2, a 5,0/2 -> 2,5.

%: dzielenie: wynikiem jest reszta niezależnie od rodzaju operandów, przy czym iloraz jest liczbą całkowitą. Jest to zatem operacja modulo.

Istnieją różne funkcje matematyczne:

double sqrt(double x)
pierwiastek kwadratowy
double cos(double x)
Cosinus
double sin(double x)
Sinus
double tan(double x)
Tangens
double pow(double x, double y)
x do potęgi y (x > 0)
double exp(double x)
Funkcja wykładnicza
double log(double x)
Logarytm naturalny
double abs(double x)
wartość bezwzględna

itd...

Wszystkie te funkcje są zdefiniowane w klasie Java o nazwie Math. Korzystając z nich, należy poprzedzić je nazwą klasy, w której są zdefiniowane. W ten sposób napiszemy:

double x, y=4;
x=Math.sqrt(y);

Definicja klasy Math jest następująca:


public  final  class  java.lang.Math
    extends  java.lang.Object  (I-§1.12)
{
        // Pola
    public final static double E;    §1.10.1
    public final static double PI;    §1.10.2

        // Metody
    public static double abs(double  a);    §1.10.3
    public static float abs(float  a);    §1.10.4
    public static int abs(int  a);    §1.10.5
    public static long abs(long  a);    §1.10.6
    public static double acos(double  a);    §1.10.7
    public static double asin(double  a);    §1.10.8
    public static double atan(double  a);    §1.10.9
    public static double atan2(double  a, double  b);    §1.10.10
    public static double ceil(double  a);    §1.10.11
    public static double cos(double  a);    §1.10.12
    public static double exp(double  a);    §1.10.13
    public static double floor(double  a);    §1.10.14
    public static double                        §1.10.15
        IEEEremainder(double  f1, double  f2);
    public static double log(double  a);    §1.10.16
    public static double max(double  a, double  b);    §1.10.17
    public static float max(float  a, float  b);    §1.10.18
    public static int max(int  a, int  b);    §1.10.19
    public static long max(long  a, long  b);    §1.10.20
    public static double min(double  a, double  b);     §1.10.21
    public static float min(float  a, float  b);    §1.10.22
    public static int min(int  a, int  b);    §1.10.23
    public static long min(long  a, long  b);    §1.10.24
    public static double pow(double  a, double  b);    §1.10.25
    public static double random();    §1.10.26
    public static double rint(double  a);    §1.10.27
    public static long round(double  a);    §1.10.28
    public static int round(float  a);    §1.10.29
    public static double sin(double  a);    §1.10.30
    public static double sqrt(double  a);    §1.10.31
    public static double tan(double  a);    §1.10.32
}

2.3.4.3. Priorytety przy obliczaniu wyrażeń arytmetycznych

Kolejność operatorów podczas obliczania wyrażenia arytmetycznego jest następująca (od najwyższego do najniższego priorytetu):

[fonctions], [ ( )], [ *, /, %], [+, -]

Operatory należące do tego samego bloku [ ] mają tę samą kolejność.

2.3.4.4. Wyrażenia relacyjne

Dostępne są następujące operatory: <, <=, ==, !=, >, >=

kolejność pierwszeństwa

>, >=, <, <=

==, !=

Wynikiem wyrażenia relacyjnego jest wartość logiczna false, jeśli wyrażenie jest fałszywe, true w przeciwnym razie.

Przykład:

      boolean fin;
      int x;
      fin=x>4;

Porównanie dwóch znaków

Niech C1 i C2 będą dwoma znakami. Można je porównać za pomocą operatorów

<, <=, ==, !=, >, >=

Wówczas porównywane są ich kody ASCII, które są liczbami. Przypomnijmy, że zgodnie z porządkiem ASCII zachodzą następujące relacje:

spacja < .. < '0' < '1' < .. < '9' < .. < 'A' < 'B' < .. < 'Z' < .. < 'a' < 'b' < .. < 'z'

Porównanie dwóch ciągów znaków

Są one porównywane znak po znaku. Pierwsza napotkana nierówność między dwoma znakami powoduje nierówność o tym samym znaczeniu dla całych ciągów znaków.

Przykłady:

Porównajmy ciągi „Chat” i „Chien”

Ta ostatnia nierówność pozwala stwierdzić, że „Kot” < „Pies”.

Image

Załóżmy, że porównujemy ciągi znaków „Kot” i „Kociak”. Przez cały czas występuje równość, aż do wyczerpania ciągu „Kot”. W tym przypadku wyczerpany ciąg uznaje się za „krótszy”. Mamy zatem relację

„Kot” < „Kociak”.

Funkcje porównywania dwóch ciągów znaków

Nie można tu używać operatorów relacyjnych <, <=, ==, !=, >, >=. Należy używać metod klasy String:

String chaine1, chaine2;
chaine1=…;
chaine2=…;
int i=chaine1.compareTo(chaine2);
boolean egal=chaine1.equals(chaine2)

W powyższym przykładzie zmienna i przyjmie wartość:

0: jeśli oba ciągi są równe

1: jeśli ciąg nr 1 > ciąg nr 2

-1: jeśli ciąg nr 1 < ciąg nr 2

Zmienna egal przyjmie wartość true, jeśli oba ciągi znaków są równe.

2.3.4.5. Wyrażenia boolowskie

Operatory to & (and) ||(or) oraz ! (not). Wynikiem wyrażenia boolowskiego jest wartość logiczna.

Kolejność operacji ! , &&, ||

przykład:

int fin;
int x;
fin= x>2 && x<4;

Operatory relacyjne mają pierwszeństwo przed operatorami && i ||.

2.3.4.6. Operacje bitowe

Operatory

Niech i oraz j będą liczbami całkowitymi.

i<<n
przesuwa i o n bitów w lewo. Bity wchodzące to zera.
i>>n
przesuwa i o n bitów w prawo. Jeśli i jest liczbą całkowitą ze znakiem (signed char, int, long), bit znaku pozostaje zachowany.
i & j
wykonuje operację logicznego bit po bicie na i i j.
i | j
wykonuje operację logicznego OR bit po bicie na i i j.
~i
uzupełnia i do 1
i^j
tworzy OU z EXCLUSIF dla i i j

Niech

    int i=0x123F, k=0xF123;
    unsigned j=0xF123;
operacja
wartość
i<<4
0x23F0
i>>4
0x0123 bit znaku zostaje zachowany.
k>>4
0xFF12 bit znaku zostaje zachowany.
i&j
0x1023
i|j
0xF33F
~i
0xEDC0

2.3.4.7. Kombinacja operatorów

a=a+b można zapisać jako a+=b

a=a-b można zapisać jako a-=b

To samo dotyczy operatorów /, %, *, <<, >>, &, |, ^

Zatem a=a+2; można zapisać jako a+=2;

2.3.4.8. Operatory inkrementacji i dekrementacji

Zapis variable++ oznacza variable=variable+1 lub też variable+=1

Zapis variable-- oznacza variable=variable-1 lub też variable-=1.

2.3.4.9. Operator?

Wyrażenie expr_cond ? expr1:expr2 jest obliczane w następujący sposób:

1: Obliczane jest wyrażenie expr_cond. Jest to wyrażenie warunkowe o wartości prawdziwej lub fałszywej

2: Jeśli jest prawdziwe, wartością wyrażenia jest wartość wyrażenia expr1. Wyrażenie expr2 nie jest obliczane.

3: Jeśli wyrażenie jest fałszywe, dzieje się odwrotnie: wartością wyrażenia jest wartość expr2. Wyrażenie expr1 nie jest obliczane.

Przykład

i=(j>4 ? j+1:j-1);

przypisze do zmiennej i:

j+1, jeśli j>4, w przeciwnym razie j-1

Jest to to samo, co zapis if(j>4) i=j+1; else i=j-1;, ale jest to bardziej zwięzłe.

2.3.4.10. Ogólna kolejność operacji

() [] funkcja
gd
! ~ ++ --
dg
new (typ) operatory rzutowania
dg
* / %
gd
+ -
gd
<< >>
gd
< <= > >= instanceof
gd
== !=
gd
&
gd
^
gd
|
gd
&&
gd
||
gd
? :
dg
= += -= itp. .
dg

gd: oznacza, że przy równej priorytetowości stosowana jest reguła „od lewej do prawej”. Oznacza to, że gdy w wyrażeniu występują operatory o tej samej priorytetowości, najpierw obliczany jest operator znajdujący się najbardziej po lewej stronie wyrażenia. dg oznacza regułę „od prawej do lewej”.

2.3.4.11. Zmiany typu

W wyrażeniu można tymczasowo zmienić reprezentację wartości. Nazywa się to zmianą typu danych lub, w języku angielskim, rzutowaniem typu (type casting). Składnia zmiany typu wartości w wyrażeniu jest następująca: (typ) wartość. Wartość przyjmuje wówczas wskazany typ. Powoduje to zmianę reprezentacji wartości.

Przykład:

int i, j;
float isurj;
isurj= (float)i/j;   // pierwszeństwo () przed /

W tym miejscu należy zmienić typ wartości i lub j na liczbę rzeczywistą, w przeciwnym razie wynik dzielenia będzie liczbą całkowitą, a nie rzeczywistą.

i jest wartością zakodowaną dokładnie na 2 bajty

(float), natomiast i jest tą samą wartością zakodowaną w przybliżeniu jako liczba zmiennoprzecinkowa na 4 bajtach

Następuje zatem transkodowanie wartości i. Transkodowanie to ma miejsce wyłącznie na czas wykonywania obliczeń, przy czym zmienna i zawsze zachowuje swój typ int.

2.4. Instrukcje kontrolujące przebieg programu

2.4.1. Zatrzymanie

Metoda exit zdefiniowana w klasie System umożliwia zatrzymanie wykonywania programu.

Składnia : void exit(int status)

działanie: zatrzymuje bieżący proces i zwraca wartość status do procesu nadrzędnego

Funkcja exit powoduje zakończenie bieżącego procesu i przekazuje kontrolę procesowi wywołującemu. Ten ostatni może wykorzystać wartość zmiennej status. W systemie DOS ta zmienna status jest zwracana do DOS w zmiennej systemowej ERRORLEVEL, której wartość można sprawdzić w pliku wsadowym. W systemie Unix to zmienna $? przechwytuje wartość status, jeśli interpreter poleceń to Bourne Shell (/bin/sh).

Przykład:

    System.exit(0);

aby zatrzymać program z wartością stanu równą 0.

2.4.2. Prosta struktura wyboru


 syntaxe :  if (condition) {actions_condition_vraie;} else {actions_condition_fausse;}

uwagi:

  • warunek jest ujęty w nawiasy.
  • każda akcja kończy się średnikiem.
  • nawiasy klamrowe nie są zakończone średnikiem.
  • nawiasy klamrowe są konieczne tylko wtedy, gdy występuje więcej niż jedna instrukcja.
  • Klauzula else może nie występować.
  • Nie ma słowa „then”.

Algorytmicznym odpowiednikiem tej struktury jest struktura „if … then … else”:

si condition
  alors actions_condition_vraie
  sinon actions_conditions_fausse
finsi

przykład


    if (x>0)  { nx=nx+1;sx=sx+x;} else dx=dx-x;

Struktury wyboru można zagnieżdżać:

if(condition1)
if (condition2)
        {......}
      else         //warunek2
         {......}
else         //warunek1
     {.......}

Czasami pojawia się następujący problem:


public static void main(void){
      int n=5;

   if(n>1)
     if(n>6)
             System.out.println(">6");
     else System.out.println("<=6");
}

W powyższym przykładzie, do którego if odnosi się else? Zasada jest taka, że kod else zawsze odnosi się do najbliższego kodu if: w tym przykładzie jest to if(n>6). Rozważmy inny przykład:


public static void main(void)
{  int n=0;

   if(n>1)
     if(n>6)   System.out.println(">6");
     else;            // else z if(n>6): nic do zrobienia 
   else System.out.println("<=1");    // else z if(n>1) 
}

W tym miejscu chcieliśmy umieścić else przy if(n>1), a nie else przy if(n>6). Z powodu powyższej uwagi jesteśmy zmuszeni umieścić instrukcję else dla if(n>6), w której nie ma żadnej instrukcji.

2.4.3. Struktura case

Składnia jest następująca:

switch(expression) {
    case v1:     
            actions1;
            break;
    case v2:     
            actions2;
            break;
         . .. .. .. .. ..
    default:     actions_sinon;
}

uwagi

  • Wartość wyrażenia kontrolnego może być wyłącznie liczbą całkowitą lub znakiem.
  • Wyrażenie kontrolne jest ujęte w nawiasy.
  • Klauzula default może nie występować.
  • Wartości vi są możliwymi wartościami wyrażenia. Jeśli wartość wyrażenia wynosi vi, wykonywane są działania następujące po klauzuli case vi.
  • Instrukcja o wartości break powoduje wyjście ze struktury case. Jeśli na końcu bloku instrukcji o wartości vi instrukcja ta nie występuje, wykonanie jest kontynuowane zgodnie z instrukcjami o wartości vi+1.

przykład

W algorytmice

selon la valeur de choix
cas 0
          arrêt
      cas 1
    exécuter module M1
  cas 2
    exécuter module M2
  sinon
    erreur<--vrai
findescas

W języku Java


    int choix, erreur;
    switch(choix){   
        case 0: System.exit(0);
      case 1: M1();break;
      case 2: M2();break;
      default: erreur=1;
     }

2.4.4. Struktura powtórzeń

2.4.4.1. Znana liczba powtórzeń

Składnia


    for (i=id;i<=if;i=i+ip){ 
       actions; 
      } 

Uwagi

  • Trzy argumenty funkcji for znajdują się w nawiasie.
  • Trzy argumenty funkcji for są oddzielone średnikami.
  • Każda akcja funkcji for kończy się średnikiem.
  • Nawias klamrowy jest konieczny tylko wtedy, gdy występuje więcej niż jedna akcja.
  • Po nawiasie klamrowym nie następuje średnik.

Algorytmicznym odpowiednikiem jest struktura pour:

pour i variant de id à if avec un pas de ip
        actions
finpour

co można przetłumaczyć na strukturę tantque:

    i  id
    tantque i<=if
        actions
        i i+ip
    fintantque

2.4.4.2. Nieznana liczba powtórzeń

W języku Java istnieje wiele struktur przeznaczonych do tego celu.

Struktura „while”


    while(condition){
          actions;
        } 

Pętla jest wykonywana tak długo, jak długo warunek jest spełniony. Pętla może nigdy nie zostać wykonana.

Uwagi:

  • warunek jest ujęty w nawiasy.
  • każda akcja kończy się średnikiem.
  • nawias klamrowy jest konieczny tylko wtedy, gdy występuje więcej niż jedna akcja.
  • po nawiasie klamrowym nie stawia się średnika.

Odpowiadającą jej strukturą algorytmiczną jest struktura „tantque”:

tantque condition
        actions
fintantque

Struktura „powtarzaj, dopóki” (do while)

Składnia jest następująca:


    do{
       instructions;
    }while(condition); 

Pętla jest wykonywana, dopóki warunek nie stanie się fałszywy lub dopóki warunek jest prawdziwy. W tym przypadku pętla jest wykonywana co najmniej raz.

Uwagi

  • warunek jest ujęty w nawiasy.
  • Każda akcja kończy się średnikiem.
  • Nawias klamrowy jest konieczny tylko wtedy, gdy występuje więcej niż jedna instrukcja.
  • po nawiasie klamrowym nie stawia się średnika.

Odpowiadającą jej strukturą algorytmiczną jest struktura „powtarzaj … aż do”:

répéter
    actions
jusqu'à condition

Struktura pętli for

Składnia jest następująca:


for(instructions_départ;condition;instructions_fin_boucle){
    instructions; 
}

Pętla jest wykonywana tak długo, jak długo warunek jest prawdziwy (oceniany przed każdą iteracją pętli). Instructions_départ są wykonywane przed pierwszym wejściem do pętli. Instructions_fin_boucle są wykonywane po każdej iteracji pętli.

uwagi

  • Trzy argumenty funkcji for znajdują się w nawiasach.
  • Trzy argumenty funkcji for są oddzielone średnikami.
  • Każda akcja w pętli for kończy się średnikiem.
  • Nawias klamrowy jest konieczny tylko wtedy, gdy występuje więcej niż jedna operacja.
  • Po nawiasie klamrowym nie następuje średnik.
  • Poszczególne instrukcje w plikach instructions_depart i instructions_fin_boucle są oddzielone przecinkami.

Odpowiednia struktura algorytmiczna wygląda następująco:

instructions_départ
tantque condition
        actions
        instructions_fin_boucle
fintantque

Przykłady

Wszystkie poniższe programy obliczają sumę pierwszych n liczb całkowitych.


1  for(i=1, somme=0;i<=n;i=i+1)
        somme=somme+a[i];

2     for (i=1, somme=0;i<=n;somme=somme+a[i], i=i+1);

3    i=1;somme=0;
    while(i<=n)
       { somme+=i; i++; }

4    i=1; somme=0;
    do somme+=i++;
         while (i<=n);

Instrukcje dotyczące zarządzania pętlą

break
wyjście z pętli for, while, do ... while.
continue
przechodzi do następnej iteracji pętli for, while, do ... while

2.5. Struktura programu w języku Java

Program w języku Java, który nie wykorzystuje klas zdefiniowanych przez użytkownika ani funkcji innych niż funkcja główna main, może mieć następującą strukturę:


public class test1{
    public static void main(String arg[]){
        … code du programme
    }// main
}// class

Funkcja main, zwana również metodą, jest wykonywana jako pierwsza podczas uruchamiania programu Java. Musi ona obowiązkowo mieć następującą sygnaturę:


    public static void main(String arg[]){

lub


    public static void main(String[] arg){

Nazwa argumentu arg może być dowolna. Jest to tablica ciągów znaków reprezentująca argumenty wiersza poleceń. Wrócimy do tego nieco później.

Jeśli używamy funkcji, które mogą generować wyjątki, których nie chcemy szczegółowo obsługiwać, możemy otoczyć kod programu klauzulą try/catch:


public class test1{
    public static void main(String arg[]){
        try{
        … code du programme
        } catch (Exception e){
            // obsługa błędów
        }// try
    }// main
}// klasa

Na początku kodu źródłowego, przed definicją klasy, często spotyka się instrukcje importujące klasy. Na przykład:


import java.io.*;
public class test1{
    public static void main(String arg[]){
        … code du programme
    }// main
}// klasa

Weźmy przykład. Niech będzie to następująca instrukcja zapisu:

System.out.println("java");

która wyświetla na ekranie java. W tej prostej instrukcji kryje się wiele elementów:

  • System to klasa, której pełna nazwa brzmi java.lang.System
  • out to właściwość tej klasy typu java.io.PrintStream, czyli innej klasy
  • println jest metodą klasy java.io.PrintStream.

Nie będziemy niepotrzebnie komplikować tego wyjaśnienia, które pojawia się zbyt wcześnie, ponieważ wymaga zrozumienia pojęcia klasy, którego jeszcze nie omówiliśmy. Klasę można porównać do zasobu. W tym przypadku kompilator będzie potrzebował dostępu do dwóch klas: java.lang.System i java.io.PrintStream. Setki klas języka Java są rozmieszczone w archiwach zwanych również pakietami (package). Instrukcje import umieszczone na początku programu służą do wskazania kompilatorowi, jakich klas zewnętrznych potrzebuje program (tych, które są używane, ale nie zostały zdefiniowane w pliku źródłowym, który ma zostać skompilowany). W naszym przykładzie program potrzebuje więc klas java.lang.System i java.io.PrintStream. Informację tę podaje się za pomocą instrukcji import. Na początku programu można by zapisać:

import java.lang.System;
import java.io.PrintStream;

Ponieważ program w języku Java często korzysta z kilkudziesięciu zewnętrznych klas, ręczne wpisywanie wszystkich niezbędnych funkcji import byłoby uciążliwe. Klasy zostały zgrupowane w pakietach, dzięki czemu można zaimportować cały pakiet. Aby więc zaimportować pakiety java.lang i java.io, należy wpisać:

import java.lang.*;
import java.io.*;

Pakiet java.lang zawiera wszystkie klasy bazowe języka Java i jest importowany automatycznie przez kompilator. W związku z tym ostatecznie wystarczy wpisać:

import java.io.*;

2.6. Obsługa wyjątków

Wiele funkcji w Javie może generować wyjątki, czyli błędy. Spotkaliśmy się już z taką funkcją – funkcją readLine:


String ligne=null;
try{
      ligne=IN.readLine();
      System.out.println("ligne="+ligne);
    } catch (Exception e){
      affiche(e);
      System.exit(2);
}// try

Gdy funkcja może wygenerować wyjątek, kompilator Java wymaga od programisty, aby ten wyjątek został obsłużony, co ma na celu uzyskanie programów bardziej odpornych na błędy: należy zawsze unikać niekontrolowanego „zawieszenia” aplikacji. W tym przypadku funkcja readLine generuje wyjątek, jeśli nie ma nic do odczytania, na przykład dlatego, że strumień wejściowy został zamknięty. Obsługa wyjątku odbywa się zgodnie z poniższym schematem:

try{
    appel de la fonction susceptible de générer l'exception
} catch (Exception e){
    traiter l'exception e
}
instruction suivante

Jeśli funkcja nie generuje wyjątku, przechodzi się do następnej instrukcji; w przeciwnym razie przechodzi się do treści klauzuli catch, a następnie do następnej instrukcji. Należy zwrócić uwagę na następujące kwestie:

  • e jest obiektem pochodnym typu Exception. Można być bardziej precyzyjnym, używając typów takich jak IOException, SecurityException, ArithmeticException itp.: istnieje około dwudziestu typów wyjątków. Pisząc catch (Exception e), wskazujemy, że chcemy obsłużyć wszystkie typy wyjątków. Jeśli kod klauzuli try może generować kilka typów wyjątków, możemy chcieć być bardziej precyzyjni, obsługując wyjątek za pomocą kilku klauzul catch:

try{
    appel de la fonction susceptible de générer l'exception
} catch (IOException e){
    traiter l'exception e
}
} catch (ArrayIndexOutOfBoundsException e){
    traiter l'exception e
}
} catch (RunTimeException e){
    traiter l'exception e
}
instruction suivante
  • Do klauzul try/catch można dodać klauzulę „finally”:
try{
    appel de la fonction susceptible de générer l'exception
} catch (Exception e){
    traiter l'exception e
}
finally{
    code exécuté après try ou catch
}
instruction suivante

W tym przypadku, niezależnie od tego, czy wystąpi wyjątek, czy nie, kod klauzuli finally zostanie zawsze wykonany.

  • Klasa Exception posiada metodę getMessage(), która zwraca komunikat opisujący wystąpiły błąd. Jeśli więc chcemy go wyświetlić, napiszemy:
catch (Exception ex){
    System.err.println("L'erreur suivante s'est produite : "+ex.getMessage());
    ...
}//catch
  • Klasa Exception posiada metodę toString(), która zwraca ciąg znaków wskazujący typ wyjątku oraz wartość właściwości Message. Można zatem napisać:
catch (Exception ex){
    System.err.println ("L'erreur suivante s'est produite : "+ex.toString());
    ...
}//catch

Można również zapisać:

catch (Exception ex){
    System.err.println ("L'erreur suivante s'est produite : "+ex);
    ...
}//catch

Mamy tu operację „string + Exception”, która zostanie automatycznie przekształcona przez kompilator na „string + Exception.toString()” w celu połączenia dwóch ciągów znaków.

Poniższy przykład przedstawia wyjątek wygenerowany w wyniku użycia nieistniejącego elementu tablicy:

// obrazy

// importy
import java.io.*;

public class tab1{
  public static void main(String[] args){
    // deklaracja i inicjalizacja tablicy
    int[] tab=new int[] {0,1,2,3};
    int i;
     // wyświetlanie tablicy za pomocą pętli for
    for (i=0; i<tab.length; i++)
      System.out.println("tab[" + i + "]=" + tab[i]);
    // generowanie wyjątku
      try{
      tab[100]=6;
    }catch (Exception e){
      System.err.println("L'erreur suivante s'est produite : " + e);
    }//try-catch
  }//main
}//klasa

Po uruchomieniu programu otrzymujemy następujące wyniki:

tab[0]=0
tab[1]=1
tab[2]=2
tab[3]=3
L'erreur suivante s'est produite : java.lang.ArrayIndexOutOfBoundsException

Oto kolejny przykład obsługi wyjątku spowodowanego przypisaniem ciągu znaków do liczby, gdy ciąg ten nie reprezentuje liczby:

// importy
import java.io.*;

public class console1{
  public static void main(String[] args){
      // utworzenie strumienia wejściowego
    BufferedReader IN=null;
    try{
        IN=new BufferedReader(new InputStreamReader(System.in));
    }catch(Exception ex){}
     // Zapytanie o nazwę
    System.out.print("Nom : ");
     // odczyt odpowiedzi
    String nom=null;
    try{
        nom=IN.readLine();
    }catch(Exception ex){}      
     // zapytanie o wiek
    int age=0;
    boolean ageOK=false;
    while ( ! ageOK){
       // pytanie
      System.out.print("âge : ");
       // odczyt i weryfikacja odpowiedzi
      try{
        age=Integer.parseInt(IN.readLine());
        ageOK=true;
      }catch(Exception ex) {
        System.err.println("Age incorrect, recommencez...");
      }//try-catch
    }//while
     // wyświetlenie końcowe
    System.out.println("Vous vous appelez " + nom + " et vous avez " + age + " ans");
  }//Main
}//klasa

Niektóre wyniki wykonania:

dos>java console1
Nom : dupont
âge : 23
Vous vous appelez dupont et vous avez 23 ans
E:\data\serge\MSNET\c#\bases\1>console1
Nom : dupont
âge : xx
Age incorrect, recommencez...
âge : 12
Vous vous appelez dupont et vous avez 12 ans

2.7. Kompilacja i uruchomienie programu Java

Należy skompilować, a następnie uruchomić następujący program:

// importowanie klas
import java.io.*;

// klasa testowa
public class coucou{
    // funkcja main
  public static void main(String args[]){
      // wyświetlanie na ekranie
    System.out.println("coucou");
  }//main
}//klasa

Plik źródłowy zawierający poprzednią klasę coucou musi nosić nazwę coucou.java:

E:\data\serge\JAVA\ESSAIS\intro1>dir
10/06/2002  08:42                  228 coucou.java

Kompilacja i uruchamianie programu Java odbywa się w oknie o nazwie DOS. Pliki wykonywalne javac.exe (kompilator) i java.exe (interpreter) znajdują się w katalogu bin w katalogu instalacyjnym JDK:

E:\data\serge\JAVA\classes\paquetages\personne>dir "e:\program files\jdk14\bin\java?.exe"
07/02/2002  12:52               24 649 java.exe
07/02/2002  12:52               28 766 javac.exe

Kompilator javac.exe przeanalizuje plik źródłowy .java i wygeneruje skompilowany plik .class. Nie jest on od razu wykonywalny przez procesor. Wymaga on interpretera języka Java (java.exe), zwanego maszyną wirtualną lub JVM (Java Virtual Machine). Na podstawie kodu pośredniego zawartego w pliku .class maszyna wirtualna wygeneruje instrukcje dostosowane do procesora komputera, na którym jest uruchomiona. Istnieją maszyny wirtualne Java dla różnych typów systemów operacyjnych (Windows, Unix, Mac OS itp.). Plik .class może być uruchomiony przez dowolną z tych maszyn wirtualnych, a zatem na dowolnym systemie operacyjnym. Ta przenośność między systemami jest jedną z głównych zalet języka Java.

Skompilujmy poprzedni program:

E:\data\serge\JAVA\ESSAIS\intro1>"e:\program files\jdk14\bin\javac" coucou.java

E:\data\serge\JAVA\ESSAIS\intro1>dir
10/06/2002  08:42                  228 coucou.java
10/06/2002  08:48                  403 coucou.class

Uruchommy wygenerowany plik .class:

E:\data\serge\JAVA\ESSAIS\intro1>"e:\program files\jdk14\bin\java" coucou
coucou

Należy zauważyć, że w powyższym poleceniu uruchomienia nie podano rozszerzenia .class pliku coucou.class, który ma zostać uruchomiony. Jest ono domyślne. Jeśli katalog bin pliku JDK znajduje się w katalogu PATH na komputerze DOS, nie trzeba podawać pełnej ścieżki do plików wykonywalnych javac.exe i java.exe. Wystarczy wówczas wpisać

javac coucou.java
java coucou

2.8. Argumenty programu głównego

Główna funkcja main przyjmuje jako parametry tablicę ciągów znaków: String[]. Tablica ta zawiera argumenty wiersza poleceń użytego do uruchomienia aplikacji. Jeśli więc uruchomimy program P za pomocą polecenia:

        java P arg0 arg1 … argn

i jeśli funkcja main jest zadeklarowana w następujący sposób:

public static void main(String[] arg);

to otrzymamy arg[0]="arg0", arg[1]="arg1" … Oto przykład:


import java.io.*;

public class param1{
  public static void main(String[] arg){
    int i;
    System.out.println("Nombre d'arguments="+arg.length);
    for (i=0;i<arg.length;i++)
      System.out.println("arg["+i+"]="+arg[i]);
  }
}

Otrzymane wyniki są następujące:

dos>java param1 a b c
Nombre d'arguments=3
arg[0]=a
arg[1]=b
arg[2]=c

2.9. Przekazywanie parametrów do funkcji

W poprzednich przykładach pokazano wyłącznie programy w języku Java zawierające tylko jedną funkcję – funkcję główną main. Poniższy przykład ilustruje, jak korzystać z funkcji oraz w jaki sposób odbywa się wymiana informacji między nimi. Parametry funkcji są zawsze przekazywane przez wartość: oznacza to, że wartość parametru rzeczywistego jest kopiowana do odpowiedniego parametru formalnego.


import java.io.*;

public class param2{
  public static void main(String[] arg){
    String S="papa";
    changeString(S);
    System.out.println("Paramètre effectif S="+S);
    int age=20;
    changeInt(age);
    System.out.println("Paramètre effectif age="+age);
  }
  private static void changeString(String S){
    S="maman";
    System.out.println("Paramètre formel S="+S);
  }
  private static void changeInt(int a){
    a=30;
    System.out.println("Paramètre formel a="+a);
  }
}

Otrzymane wyniki są następujące:

Paramètre formel S=maman
Paramètre effectif S=papa
Paramètre formel a=30
Paramètre effectif age=20

Wartości parametrów rzeczywistych „papa” i 20 zostały skopiowane do parametrów formalnych S i a. Następnie parametry te zostały zmodyfikowane. Parametry rzeczywiste pozostały natomiast niezmienione. Należy tutaj zwrócić uwagę na typ parametrów rzeczywistych:

  • S jest odwołaniem do obiektu c.a.d, czyli adresem obiektu w pamięci
  • age to wartość całkowita

2.10. Przykład dotyczący podatków

Zakończymy ten rozdział przykładem, do którego będziemy wielokrotnie wracać w niniejszym dokumencie. Zamierzamy napisać program umożliwiający obliczenie podatku podatnika. Rozważymy uproszczony przypadek podatnika, który ma do zadeklarowania wyłącznie swoje wynagrodzenie:

  • liczba udziałów pracownika wynosi nbParts=nbEnfants/2 +1, jeśli jest on osobą stanu wolnego, nbEnfants/2+2, jeśli jest żonaty, gdzie nbEnfants to liczba jego dzieci.
  • jeśli ma co najmniej troje dzieci, przysługuje mu dodatkowa połowa udziału
  • oblicza się jego dochód podlegający opodatkowaniu R = 0,72 * S, gdzie S to jego roczne wynagrodzenie
  • oblicza się jego współczynnik rodzinny QF = R / nbParts
  • oblicza się jego podatek I. Rozważmy następującą tabelę:
12620,0
0
0
13190
0,05
631
15640
0,1
1290,5
24 740
0,15
2072,5
31810
0,2
3309,5
39 970
0,25
4900
48360
0,3
6898,5
55790
0,35
9316,5
92970
0,4
12106
127860
0,45
16754,5
151250
0,50
23147,5
172 040
0,55
30710
195 000
0,60
39312
0
0,65
49062

Każdy wiersz zawiera 3 pola. Aby obliczyć podatek I, należy znaleźć pierwszy wiersz, w którym QF <= pole1. Na przykład, jeśli QF = 23000, znajdziemy wiersz

    24740        0.15        2072.5

Podatek I wynosi wówczas 0,15*R – 2072,5*nbParts. Jeśli QF jest takie, że relacja QF <= pole1 nigdy nie jest spełniona, wówczas stosuje się współczynniki z ostatniego wiersza. W tym przypadku:

    0                0.65        49062

co daje podatek I = 0,65*R – 49062*nbParts.

Odpowiedni program w języku Java wygląda następująco:


import java.io.*;

public class impots{

    // ------------ main
    public static void main(String arg[]){

        // dane

        // granice przedziałów podatkowych
        double Limites[]={12620, 13190, 15640, 24740, 31810, 39970, 48360,55790, 92970, 127860, 151250, 172040, 195000, 0};    
        // współczynnik stosowany do liczby udziałów
        double Coeffn[]={0, 631, 1290.5, 2072.5, 3309.5, 4900, 6898.5, 9316.5,12106, 16754.5, 23147.5, 30710, 39312, 49062};

        // program

        // tworzenie strumienia danych wprowadzanych z klawiatury
        BufferedReader IN=null;
        try{
            IN=new BufferedReader(new InputStreamReader(System.in));
        }
        catch(Exception e){
            erreur("Création du flux d'entrée", e, 1);
        }

            // pobieranie stanu cywilnego
        boolean OK=false;
        String reponse=null;
        while(! OK){
            try{
                System.out.print("Etes-vous marié(e) (O/N) ? ");
                reponse=IN.readLine();
                reponse=reponse.trim().toLowerCase();
                if (! reponse.equals("o") && !reponse.equals("n"))
                    System.out.println("Réponse incorrecte. Recommencez");
                else OK=true;
            } catch(Exception e){
                erreur("Lecture état marital",e,2);
            }
        }
        boolean Marie = reponse.equals("o");

            // liczba dzieci
        OK=false;
        int NbEnfants=0; 
        while(! OK){
            try{
                System.out.print("Nombre d'enfants : ");
                reponse=IN.readLine();
                try{
                    NbEnfants=Integer.parseInt(reponse);
                    if(NbEnfants>=0) OK=true; 
                        else System.err.println("Réponse incorrecte. Recommencez");
                } catch(Exception e){
                    System.err.println("Réponse incorrecte. Recommencez");
                }// try
            } catch(Exception e){
                erreur("Lecture état marital",e,2);
            }// try
        }// while

        // wynagrodzenie
        OK=false;
        long Salaire=0; 
        while(! OK){
            try{
                System.out.print("Salaire annuel : ");
                reponse=IN.readLine();
                try{
                    Salaire=Long.parseLong(reponse);
                    if(Salaire>=0)  OK=true;
                        else System.err.println("Réponse incorrecte. Recommencez");
                } catch(Exception e){
                    System.err.println("Réponse incorrecte. Recommencez");
                }// try
            } catch(Exception e){
                erreur("Lecture Salaire",e,4);
            }// try
        }// while

            // obliczanie liczby udziałów
        double NbParts;
        if(Marie) NbParts=(double)NbEnfants/2+2;
            else NbParts=(double)NbEnfants/2+1;
        if (NbEnfants>=3) NbParts+=0.5;
        
            // dochód podlegający opodatkowaniu
        double Revenu;
        Revenu=0.72*Salaire;

            // współczynnik rodzinny
        double QF;
        QF=Revenu/NbParts;
        
            // wyszukiwanie przedziału podatkowego odpowiadającego QF
        int i;
        int NbTranches=Limites.length;
        Limites[NbTranches-1]=QF;
        i=0;
        while(QF>Limites[i]) i++;
            // podatek
        long impots=(long)(i*0.05*Revenu-Coeffn[i]*NbParts);

        // wyświetlanie wyniku
        System.out.println("Impôt à payer : " + impots);
    }// główna

    // ------------ błąd
    private static void erreur(String msg, Exception e, int exitCode){
        System.err.println(msg+"("+e+")");
        System.exit(exitCode);
    }// błąd

}// klasa

Otrzymane wyniki są następujące:

C:\Serge\java\impots\1>java impots

Etes-vous marié(e) (O/N) ? o
Nombre d'enfants : 3
Salaire annuel : 200000
Impôt à payer : 16400

C:\Serge\java\impots\1>java impots

Etes-vous marié(e) (O/N) ? n
Nombre d'enfants : 2
Salaire annuel : 200000
Impôt à payer : 33388

C:\Serge\java\impots\1>java impots

Etes-vous marié(e) (O/N) ? w
Réponse incorrecte. Recommencez
Etes-vous marié(e) (O/N) ? q
Réponse incorrecte. Recommencez
Etes-vous marié(e) (O/N) ? o
Nombre d'enfants : q
Réponse incorrecte. Recommencez
Nombre d'enfants : 2
Salaire annuel : q
Réponse incorrecte. Recommencez
Salaire annuel : 1
Impôt à payer : 0