2. Основи мови Java
2.1. Вступ
Спочатку ми розглядаємо Java як класичну мову програмування. Про об’єкти ми поговоримо пізніше.
У програмі є дві складові
- дані
- інструкції, що оперують ними
Зазвичай намагаються відокремити дані від інструкцій:

2.2. Дані в Java
Java використовує такі типи даних:
- цілі числа
- дійсні числа
- символи та символьні рядки
- логічні значення
- об'єкти
2.2.1. Заздалегідь визначені типи даних
Тип | Кодування | Діапазон |
char | 2 байти | символ Unicode |
int | 4 байти | [-231, 231-1] |
long | 8 байт | [-263, 263 -1] |
byte | 1 байт | [-27 , 27 -1] |
short | 2 байти | [-215, 215-1] |
float | 4 байти | [3.4 10-38, 3.4 10+38] в абсолютному значенні |
double | 8 байт | [1.7 10-308 , 1.7 10+308] в абсолютному значенні |
boolean | 1 біт | true, false |
String | посилання на об'єкт | рядок символів |
Date | посилання на об'єкт | дата |
Character | посилання на об'єкт | символ |
Integer | посилання на об'єкт | int |
Long | посилання на об'єкт | long |
Byte | посилання на об'єкт | байт |
Float | посилання на об'єкт | float |
Double | посилання на об'єкт | double |
Boolean | посилання на об'єкт | boolean |
2.2.2. Запис літеральних даних
entier | 145, -7, 0xFF (шестнадцяткове) |
число типу «double» | 134,789, -45E-18 (-45 × 10⁻¹⁸) |
число з плаваючою комою (float) | 134.789F, -45E-18F (-45 × 10⁻¹⁸) |
caractère | 'A', 'b' |
рядок | "сьогодні" |
booléen | true, false |
date | new Date(13,10,1954) (день, місяць, рік) |
2.2.3. Подання даних
2.2.3.1. Роль оголошень
Програма оперує даними, що характеризуються іменем та типом. Ці дані зберігаються в пам'яті. Під час компіляції програми компілятор призначає кожному елементу даних місце в пам'яті, що характеризується адресою та розміром. Він робить це, спираючись на оголошення, зроблені програмістом.
Крім того, ці оголошення дозволяють компілятору виявляти помилки в програмі. Так, операція
x=x*2;
буде визнана помилковою, якщо, наприклад, x є символьним рядком.
2.2.3.2. Оголошення констант
Синтаксис оголошення константи такий:
final type ім'я = значення; //визначає константу ім'я=значення
приклад: final float PI=3.141592F;
Примітка
Навіщо оголошувати константи?
- Програму буде легше читати, якщо надати константі змістовне ім’я:
наприклад: final float taux_tva=0.186F;
- Вносити зміни до програми буде простіше, якщо «константа» раптом зміниться. Так, у наведеному вище прикладі, якщо ставка ПДВ зросте до 33%, єдиною зміною, яку потрібно буде внести, буде зміна оператора, що визначає її значення:
final float taux_tva=0.33F;
Якби в програмі явно використовувалося значення 0,186, то довелося б змінювати безліч операторів.
2.2.3.3. Оголошення змінних
Змінна ідентифікується за іменем і належить до певного типу даних. Ім’я змінної в Java складається з n символів: перший — літера, решта — літери або цифри. Java розрізняє великі та малі літери. Отже, змінні FIN та fin є різними.
Змінні можна ініціалізувати під час їх оголошення. Синтаксис оголошення однієї або декількох змінних такий:
де identificateur_de_type — це попередньо визначений тип або об’єктний тип, визначений програмістом.
2.2.4. Перетворення між числами та символьними рядками
число -> рядок | "" + число |
рядок -> ціле | Integer.parseInt(рядок) |
рядок -> long | Long.parseLong(рядок) |
рядок -> double | Double.valueOf(рядок).doubleValue() |
рядок -> float | Float.valueOf(рядок).floatValue() |
Ось програма, що демонструє основні методи перетворення чисел у рядки та навпаки. Перетворення рядка на число може завершитися невдачею, якщо рядок не є дійсним числом. У такому випадку виникає фатальна помилка, яка в Java називається «винятком». Цю помилку можна обробити за допомогою наступного оператора try/catch:
try{
appel de la fonction susceptible de générer l'exception
} catch (Exception e){
traiter l'exception e
}
instruction suivante
Якщо функція не генерує винятку, то виконується наступна інструкція; в іншому випадку виконується тіло блоку catch, а потім — наступна інструкція. До питання обробки винятків ми повернемося пізніше.
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;
// число --> рядок
S=""+i;
affiche(S);
S=""+l;
affiche(S);
S=""+f;
affiche(S);
S=""+d;
affiche(S);
//логічне значення --> рядок
final boolean b=false;
S=""+new Boolean(b);
affiche(S);
// рядок --> ціле
int i1;
i1=Integer.parseInt("10");
affiche(""+i1);
try{
i1=Integer.parseInt("10.67");
affiche(""+i1);
} catch (Exception e){
affiche("Erreur "+e);
}
// рядок --> long
long l1;
l1=Long.parseLong("100");
affiche(""+l1);
try{
l1=Long.parseLong("10.675");
affiche(""+l1);
} catch (Exception e){
affiche("Erreur "+e);
}
// рядок --> 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);
}
// рядок --> float
float f1;
f1=Float.valueOf("100.87").floatValue();
affiche(""+f1);
try{
d1=Float.valueOf("abcd").floatValue();
affiche(""+f1);
} catch (Exception e){
affiche("Erreur "+e);
}
}// кінець main
public static void affiche(String S){
System.out.println("S="+S);
}
}// кінець класу
Отримано такі результати:
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. Масиви даних
Масив у Java — це об’єкт, що дозволяє об’єднати дані одного типу під одним ідентифікатором. Його оголошення має такий вигляд:
Тип Масив[] = new Тип[n] або Тип[] Масив = new Тип[n]
Обидва синтаксиси є допустимими. n — це кількість елементів, які може містити масив. Синтаксис Tableau[i] позначає елемент № i, де i належить до інтервалу [0,n-1]. Будь-яке посилання на елемент Tableau[i], де i не належить до інтервалу [0,n-1], спричинить виняток.
Двовимірний масив можна оголосити наступним чином:
Type Tableau[][] = new Type[n][p] або Type[][] Tableau = new Type[n][p]
Синтаксис Tableau[i] позначає елемент № i масиву Tableau, де i належить до інтервалу [0,n-1]. Таблиця[i] сама по собі є таблицею: Таблиця[i][j] позначає дані № j з масиву Tableau[i], де j належить до інтервалу [0,p-1]. Будь-яке посилання на дані з масиву Tableau із неправильними індексами призводить до фатальної помилки.
Ось приклад:
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]);
}
}
та результати виконання:
Масив — це об’єкт, що має атрибут length: це розмір масиву.
2.3. Елементарні оператори мови Java
Розрізняють
- елементарні оператори, що виконуються комп’ютером,
- команди, що керують ходом виконання програми.
Елементарні інструкції стають зрозумілими, якщо розглянути структуру мікрокомп’ютера та його периферійних пристроїв.

-
зчитування інформації з клавіатури
-
обробка інформації
-
виведення інформації на екран
-
зчитування інформації з файлу на диску
-
запис інформації у файл на диску
2.3.1. Виведення на екран
Синтаксис команди запису на екран такий:
System.out.println(вираз) або System.err.println(вираз)
де expression — це будь-який тип даних, який можна перетворити на рядок символів для відображення на екрані. У попередньому прикладі ми розглянули дві команди запису:
System.out записує дані у текстовий файл, яким за замовчуванням є екран. Те саме стосується System.err. Ці файли мають номери (або descripteur) відповідно 1 і 2. Потік введення з клавіатури (System.in) також розглядається як текстовий файл із дескриптором 0. Як DOS, так і Unix підтримують передачу даних між командами за допомогою труби (pipe):
Усе, що commande1 записує за допомогою System.out, передається (перенаправляється) у вхід System.in програми commande2. Інакше кажучи, commande2 зчитує за допомогою System.in дані, згенеровані commande2 за допомогою System.out, які, отже, більше не відображаються на екрані. Ця система широко використовується в Unix. У цьому ланцюжку команд потік System.err не перенаправляється: він виводить інформацію на екран. Саме тому його використовують для виведення повідомлень про помилки (звідси й його назва err): ми маємо гарантію, що під час перенаправлення команд повідомлення про помилки й надалі відображатимуться на екрані. Тому варто звикнути виводити повідомлення про помилки на екран за допомогою потоку System.err, а не потоку System.out.
2.3.2. Зчитування даних, введених з клавіатури
Потік даних, що надходить з клавіатури, позначається об’єктом System.in типу InputStream. Цей тип об’єктів дозволяє зчитувати дані посимвольно. Далі програміст повинен самостійно знайти в цьому потоці символів інформацію, яка його цікавить. Тип InputStream не дозволяє зчитувати рядок тексту одним прийомом. Тип BufferedReader дозволяє це робити за допомогою методу readLine.
Щоб мати змогу зчитувати рядки тексту, введені з клавіатури, на основі вхідного потоку System.in типу InputStream створюється інший вхідний потік, цього разу типу BufferedReader:
Ми не будемо тут пояснювати деталі цієї інструкції, яка пов’язана з поняттям побудови об’єктів. Ми будемо використовувати її такою, як є.
Створення потоку може завершитися невдачею: у такому разі генерується фатальна помилка, яка в Java називається винятком. Щоразу, коли метод може генерувати виняток, компілятор Java вимагає, щоб програміст його обробив. Отже, щоб створити попередній вхідний потік, насправді потрібно написати:
BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
System.err.println("Erreur " +e);
System.exit(1);
}
Знову ж таки, ми не будемо тут пояснювати обробку винятків. Після створення попереднього потоку IN можна прочитати один рядок тексту за допомогою команди:
Рядок, введений з клавіатури, зберігається у змінній ligne і потім може бути використаний програмою.
2.3.3. Приклад операцій введення-виведення
Ось програма, що ілюструє операції введення-виведення з клавіатури та на екран:
import java.io.*; // необхідно для використання потоків вводу-виводу
public class io1{
public static void main (String[] arg){
// запис у потік System.out
Object obj=new Object();
System.out.println(""+obj);
System.out.println(obj.getClass().getName());
// запис у потік System.err
int i=10;
System.err.println("i="+i);
// читання рядка, введеного з клавіатури
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);
}
}//кінець введення
public static void affiche(Exception e){
System.err.println("Erreur : "+e);
}
}//кінець класу
та результати виконання:
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à
Інструкції
мають на меті показати, що будь-який об’єкт може бути виведений на екран. Ми не будемо тут пояснювати значення того, що виводиться. У блоці також є виведення значення об’єкта:
try{
IN=new BufferedReader(new InputStreamReader(System.in));
} catch (Exception e){
affiche(e);
System.exit(1);
}
Змінна e є об’єктом типу Exception, який тут відображається за допомогою виклику affiche(e). Ми вже стикалися, хоча й не згадували про це, з таким відображенням значення винятку в програмі конвертації, яку розглядали вище.
2.3.4. Присвоєння значення виразу змінній
Тут нас цікавить операція variable=expression;
Вираз може бути такого типу: арифметичний, відносний, булевий, символьний
2.3.4.1. Інтерпретація операції присвоювання
Операція variable=expression; сама по собі є виразом, обчислення якого відбувається наступним чином:
- Права частина присвоювання обчислюється: результатом є значення V.
- значення V присвоюється змінній
- значення V також є значенням операції присвоєння, яка цього разу розглядається як вираз.
Таким чином, операція V1=V2=вираз є допустимою. Через пріоритет обчислюватися буде оператор =, що знаходиться найдалі праворуч. Отже, маємо V1=(V2=вираз). Вираз V2=expression обчислюється і має значення V. Обчислення цього виразу призвело до присвоєння V значення V2. Наступний оператор = обчислюється у вигляді V1=V. Значенням цього виразу знову є V. Його обчислення призводить до присвоєння V змінній V1. Отже, операція V1=V2=вираз є виразом, обчислення
1: призводить до присвоєння значення expression змінним V1 та V2
2: повертає як результат значення expression.
Можна узагальнити це на вираз типу: V1=V2=....=Vn=вираз
2.3.4.2. Арифметичний вираз
Оператори арифметичних виразів такі:
+: додавання
- : віднімання
*: множення
/ : ділення: результатом є точний частка, якщо хоча б один з операндів є дійсним числом. Якщо обидва операнди є цілими числами, результатом є ціла частка. Таким чином, 5/2 -> 2, а 5,0/2 -> 2,5.
% : ділення: результатом є залишок незалежно від типу операндів, причому частка є цілим числом. Отже, це операція за модулем.
Існують різні математичні функції:
double sqrt(double x) | квадратний корінь |
double cos(double x) | Косинус |
double sin(double x) | Синус |
double tan(double x) | Тангенс |
double pow(double x, double y) | x у степені y (x>0) |
double exp(double x) | Експонента |
double log(double x) | логарифм по основі 10 |
double abs(double x) | абсолютне значення |
тощо...
Усі ці функції визначені в класі Java під назвою Math. При їх використанні перед ними слід ставити префікс із назвою класу, в якому вони визначені. Отже, ми напишемо:
Визначення класу Math таке:
public final class java.lang.Math
extends java.lang.Object (I-§1.12)
{
// Поля
public final static double E; §1.10.1
public final static double PI; §1.10.2
// Методи
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. Пріоритети при обчисленні арифметичних виразів
Пріоритет операторів під час обчислення арифметичного виразу є таким (від найвищого до найнижчого):
[fonctions], [ ( )], [ *, /, %], [+, -]
Оператори одного блоку [ ] мають однаковий пріоритет.
2.3.4.4. Реляційні вирази
Використовуються такі оператори: <, <=, ==, !=, >, >=
порядок пріоритету
>, >=, <, <=
==, !=
Результатом відносного виразу є логічне значення false, якщо вираз неправдивий, true — в іншому випадку.
Приклад:
Порівняння двох символів
Нехай є два символи C1 та C2. Їх можна порівняти за допомогою операторів
<, <=, ==, !=, >, >=
У цьому випадку порівнюються їхні коди ASCII, які є числами. Нагадаємо, що згідно з порядком ASCII маємо такі відношення:
пробіл < .. < '0' < '1' < .. < '9' < .. < 'A' < 'B' < .. < 'Z' < .. < 'a' < 'b' < .. < 'z'
Порівняння двох символьних рядків
Вони порівнюються символ за символом. Перша нерівність, виявлена між двома символами, зумовлює нерівність того самого знака для рядків.
Приклади:
Порівняємо рядки «Chat» і «Chien»
Остання нерівність дозволяє стверджувати, що «Chat» < «Chien».

Нехай потрібно порівняти рядки «Chat» і «Chaton». Рядки збігаються аж до вичерпання рядка «Chat». У цьому випадку вичерпаний рядок визнається «меншим». Отже, маємо співвідношення
«Кіт» < «Кошеня».
Функції порівняння двох рядків
Тут не можна використовувати відносні оператори <, <=, ==, !=, >, >=. Необхідно використовувати методи класу String:
String chaine1, chaine2;
chaine1=…;
chaine2=…;
int i=chaine1.compareTo(chaine2);
boolean egal=chaine1.equals(chaine2)
У наведеному вище прикладі змінна i матиме значення:
0: якщо обидва рядки рівні
1: якщо рядок № 1 > рядок № 2
-1: якщо рядок № 1 < рядок № 2
Змінна egal матиме значення true, якщо обидва рядки збігаються.
2.3.4.5. Булеві вирази
Оператори — це & (and) ||(or) та ! (not). Результатом булевого виразу є булеве значення.
Порядок пріоритетів ! , &&, ||
приклад:
Оператори порівняння мають пріоритет над операторами && та ||.
2.3.4.6. Бітова обробка
Оператори
Нехай i та j — два цілі числа.
i<<n | зсуває i на n бітів вліво. Біти, що надходять, дорівнюють нулям. |
i>>n | зсуває i на n бітів вправо. Якщо i є цілим числом зі знаком (signed char, int, long), біт знака зберігається. |
i & j | виконує побітову логічну операцію «і» над i та j. |
i | j | виконує побітову логічну операцію «АБО» над i та j. |
~i | доповнює i до 1 |
i^j | виконує операцію OU EXCLUSIF над i та j |
Нехай
операція | значення |
i<<4 | 0x23F0 |
i>>4 | 0x0123 біт знака зберігається. |
k>>4 | 0xFF12 біт знака зберігається. |
i&j | 0x1023 |
i|j | 0xF33F |
~i | 0xEDC0 |
2.3.4.7. Комбінація операторів
a=a+b можна записати як a+=b
a=a-b можна записати як a-=b
Те саме стосується операторів /, %, *, <<, >>, &, |, ^
Таким чином, a=a+2; можна записати як a+=2;
2.3.4.8. Оператори інкрементування та декрементування
Запис variable++ означає variable=variable+1 або variable+=1
Запис variable-- означає variable=variable-1 або variable-=1.
2.3.4.9. Оператор?
Вираз expr_cond ? expr1:expr2 обчислюється наступним чином:
1: обчислюється вираз expr_cond. Це умовний вираз, який може мати значення «істина» або «хиба»
2: Якщо він істинний, значенням виразу стає значення expr1. expr2 не обчислюється.
3: Якщо вираз істинний, відбувається зворотне: значенням виразу стає значення expr2. expr1 не обчислюється.
Приклад
i=(j>4 ? j+1:j-1);
присвоїть змінній i:
j+1, якщо j>4, інакше — j-1
Це те саме, що написати if(j>4) i=j+1; else i=j-1; але це лаконічніше.
2.3.4.10. Загальний порядок пріоритету операторів
() [] функція | gd |
! ~ ++ -- | dg |
new (тип) оператори приведення типу | dg |
* / % | gd |
+ - | gd |
<< >> | gd |
< <= > >= instanceof | gd |
== != | gd |
& | gd |
^ | gd |
| | gd |
&& | gd |
|| | gd |
? : | dg |
= += -= тощо. | dg |
gd: вказує, що при рівному пріоритеті дотримується порядок зліва направо. Це означає, що коли у виразі є оператори з однаковим пріоритетом, то спочатку обчислюється оператор, розташований найдалі ліворуч у виразі. dg вказує на порядок справа наліво.
2.3.4.11. Зміни типу
У виразі можна тимчасово змінити кодування значення. Це називається зміною типу даних або, англійською, type casting. Синтаксис зміни типу значення у виразі такий: (тип) значення. Значення набуває вказаного типу. Це призводить до зміни кодування значення.
Приклад:
Тут необхідно змінити тип i або j на дійсний, інакше в результаті ділення отримаємо цілочисельний, а не дійсний результат.
i — це значення, точно закодоване у 2 байтах
(float), а i — це те саме значення, закодоване з наближенням у вигляді дійсного числа на 4 байти
Отже, відбувається перекодування значення i. Це перекодування відбувається лише на час обчислення, а змінна i завжди зберігає свій тип int.
2.4. Інструкції для контролю ходу виконання програми
2.4.1. Зупинка
Метод exit, визначений у класі System, дозволяє зупинити виконання програми.
Дія: зупиняє поточний процес і повертає значення status батьківському процесу
exit викликає завершення поточного процесу та передає управління процесу, що викликав. Значення status може бути використане цим процесом. У DOS ця змінна status повертається до DOS у системній змінній ERRORLEVEL, значення якої можна перевірити у командному файлі. У Unix саме змінна $? отримує значення status, якщо командним інтерпретатором є Bourne Shell (/bin/sh).
Приклад:
для зупинки програми зі станом 0.
2.4.2. Проста структура вибору
syntaxe : if (condition) {actions_condition_vraie;} else {actions_condition_fausse;}
Примітки:
- умова взята в дужки.
- кожна дія закінчується крапкою з комою.
- фігурні дужки не закінчуються крапкою з комою.
- фігурні дужки потрібні лише в тому випадку, якщо є більше ніж одна дія.
- клаузула else може бути відсутньою.
- Слова then немає.
Алгоритмічним еквівалентом цієї структури є структура «якщо … то … інакше»:
приклад
if (x>0) { nx=nx+1;sx=sx+x;} else dx=dx-x;
Структури вибору можна вкладати одна в одну:
Іноді виникає така проблема:
public static void main(void){
int n=5;
if(n>1)
if(n>6)
System.out.println(">6");
else System.out.println("<=6");
}
У попередньому прикладі, до якого if відноситься else? За правилом, else завжди пов'язаний із найближчим if: у цьому прикладі — з if(n>6). Розглянемо інший приклад:
public static void main(void)
{ int n=0;
if(n>1)
if(n>6) System.out.println(">6");
else; // else з if(n>6): нічого не робити
else System.out.println("<=1"); // else з if(n>1)
}
Тут ми хотіли розмістити else у if(n>1), а не else у if(n>6). Через попереднє зауваження ми змушені додати оператор else для if(n>6), у якому немає жодної інструкції.
2.4.3. Структура case
Синтаксис такий:
switch(expression) {
case v1:
actions1;
break;
case v2:
actions2;
break;
. .. .. .. .. ..
default: actions_sinon;
}
примітки
- Значенням контрольного виразу може бути лише ціле число або символ.
- Контрольний вираз поміщений у дужки.
- Умова default може бути відсутньою.
- Значення vi є можливими значеннями виразу. Якщо значення виразу дорівнює vi, виконуються дії, що йдуть після клаузули case vi.
- Інструкція break виводить із структури case. Якщо вона відсутня в кінці блоку інструкцій із значенням vi, виконання продовжується інструкціями із значенням vi+1.
Приклад
В алгоритміці
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
На 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. Структура повторення
2.4.4.1. Відома кількість повторень
Синтаксис
for (i=id;i<=if;i=i+ip){
actions;
}
Примітки
- 3 аргументи функції for знаходяться в дужках.
- 3 аргументи функції for розділені крапками з комою.
- Кожна дія функції for закінчується крапкою з комою.
- фігурна дужка потрібна лише в тому випадку, якщо є більше ніж одна дія.
- за фігурною дужкою не ставиться крапка з комою.
Алгоритмічним еквівалентом є структура pour:
яку можна перетворити на структуру tantque:
2.4.4.2. Кількість повторень невідома
У Java існує безліч структур для цього випадку.
Конструкція while
while(condition){
actions;
}
Цикл виконується, доки виконується умова. Цикл може ніколи не виконатися.
Примітки:
- умова взята в дужки.
- кожна дія закінчується крапкою з комою.
- фігурна дужка потрібна лише в тому випадку, якщо є більше ніж одна дія.
- після фігурної дужки не ставиться крапка з комою.
Відповідною алгоритмічною структурою є структура «доки»:
Структура «повторювати до» (do while)
Синтаксис такий:
do{
instructions;
}while(condition);
Цикл виконується доти, доки умова не стане хибною або доки умова залишається істинною. У цьому випадку цикл виконується щонайменше один раз.
Примітки
- умова взята в дужки.
- Кожна дія закінчується крапкою з комою.
- фігурні дужки потрібні лише в тому випадку, якщо є більше ніж одна дія.
- після фігурної дужки не ставиться крапка з комою.
Відповідна алгоритмічна структура — це структура «повторювати … до»:
Структура «for»
Синтаксис такий:
for(instructions_départ;condition;instructions_fin_boucle){
instructions;
}
Цикл виконується, доки умова є істинною (оцінюється перед кожним проходом циклу). Instructions_départ виконуються перед першим входом у цикл. Instructions_fin_boucle виконуються після кожного проходу циклу.
Примітки
- 3 аргументи функції for знаходяться в дужках.
- 3 аргументи функції for розділені крапками з комою.
- Кожна дія циклу for закінчується крапкою з комою.
- фігурна дужка потрібна лише в тому випадку, якщо є більше ніж одна дія.
- за фігурною дужкою не ставиться крапка з комою.
- різні інструкції у файлах instructions_depart та instructions_fin_boucle розділені комами.
Відповідна алгоритмічна структура така:
Приклади
Усі наведені нижче програми обчислюють суму перших n цілих чисел.
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);
Інструкції з управління циклом
break | виходить із циклу for, while, do ... while. |
continue | переходить до наступної ітерації циклів for, while, do ... while |
2.5. Структура програми на Java
Програма на Java, яка не використовує класів, визначених користувачем, та функцій, окрім головної функції main, може мати таку структуру:
public class test1{
public static void main(String arg[]){
… code du programme
}// main
}// class
Функція main, яку також називають методом, виконується першою під час виконання програми Java. Вона обов’язково повинна мати такий сигнатур:
public static void main(String arg[]){
або
public static void main(String[] arg){
Ім'я аргументу arg може бути будь-яким. Це масив символьних рядків, що представляють аргументи командного рядка. Ми повернемося до цього трохи пізніше.
Якщо використовуються функції, які можуть генерувати винятки, які не бажано обробляти детально, код програми можна обгорнути в блок try/catch:
public class test1{
public static void main(String arg[]){
try{
… code du programme
} catch (Exception e){
// обробка помилок
}// try
}// main
}// клас
На початку вихідного коду та перед визначенням класу зазвичай розміщуються оператори імпорту класів. Наприклад:
import java.io.*;
public class test1{
public static void main(String arg[]){
… code du programme
}// main
}// клас
Розглянемо приклад. Нехай маємо таку інструкцію запису:
яка виводить на екран java. У цій простій інструкції міститься багато інформації:
- System — це клас, повна назва якого java.lang.System
- out — це властивість цього класу типу java.io.PrintStream, іншого класу
- println — це метод класу java.io.PrintStream.
Ми не будемо зайво ускладнювати це пояснення, яке з’являється занадто рано, оскільки воно вимагає розуміння поняття «клас», яке ще не розглядалося. Клас можна порівняти з ресурсом. У цьому випадку компілятору знадобиться доступ до двох класів: java.lang.System та java.io.PrintStream. Сотні класів Java розподілені по архівах, які також називаються пакетами (package). Інструкції import, розміщені на початку програми, слугують для того, щоб вказати компілятору, які зовнішні класи потрібні програмі (ті, що використовуються, але не визначені у вихідному файлі, який буде компілюватися). Отже, у нашому прикладі програмі потрібні класи java.lang.System та java.io.PrintStream. Про це повідомляється за допомогою інструкції import. На початку програми можна написати:
Оскільки Java-програма зазвичай використовує кілька десятків зовнішніх класів, було б складно писати всі необхідні функції import. Класи об’єднано в пакети, тож можна імпортувати цілий пакет. Отже, щоб імпортувати пакети java.lang та java.io, потрібно написати:
Пакет java.lang містить усі базові класи Java і імпортується компілятором автоматично. Тому, зрештою, потрібно написати лише:
2.6. Обробка винятків
Багато функцій Java можуть генерувати винятки, тобто помилки. Ми вже зустрічали таку функцію — функцію readLine:
String ligne=null;
try{
ligne=IN.readLine();
System.out.println("ligne="+ligne);
} catch (Exception e){
affiche(e);
System.exit(2);
}// try
Коли функція може генерувати виняток, компілятор Java зобов'язує програміста обробляти його з метою отримання програм, більш стійких до помилок: завжди слід уникати неконтрольованого «зависання» додатка. У цьому випадку функція readLine генерує виняток, якщо немає чого читати, наприклад, через те, що вхідний потік було закрито. Обробка винятку здійснюється за такою схемою:
try{
appel de la fonction susceptible de générer l'exception
} catch (Exception e){
traiter l'exception e
}
instruction suivante
Якщо функція не генерує винятку, то виконується наступна інструкція; в іншому випадку виконується тіло блоку catch, а потім — наступна інструкція. Зверніть увагу на наступні моменти:
- e — це об’єкт, похідний від типу Exception. Можна бути точнішим, використовуючи такі типи, як IOException, SecurityException, ArithmeticException тощо: існує близько двадцяти типів винятків. Записуючи catch (Exception e), ми вказуємо, що хочемо обробляти всі типи винятків. Якщо код блоку try може генерувати кілька типів винятків, можна бути більш конкретним, обробляючи виняток за допомогою кількох блоків 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
- До умов try/catch можна додати умову 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
У цьому випадку, незалежно від того, чи виникне виняток, код клаузули finally завжди буде виконано.
- Клас Exception має метод getMessage(), який повертає повідомлення з детальною інформацією про помилку, що сталася. Отже, якщо ми хочемо відобразити це повідомлення, ми напишемо:
catch (Exception ex){
System.err.println("L'erreur suivante s'est produite : "+ex.getMessage());
...
}//catch
- Клас Exception має метод toString(), який повертає рядок, що вказує тип винятку, а також значення властивості Message. Таким чином, можна написати:
catch (Exception ex){
System.err.println ("L'erreur suivante s'est produite : "+ex.toString());
...
}//catch
Також можна записати:
Тут ми маємо операцію «рядок + Exception», яку компілятор автоматично перетворить на «рядок + Exception.toString()» для об’єднання двох рядків.
Наступний приклад демонструє виняток, що виникає внаслідок використання неіснуючого елемента масиву:
// картини
// імпорт
import java.io.*;
public class tab1{
public static void main(String[] args){
// оголошення та ініціалізація масиву
int[] tab=new int[] {0,1,2,3};
int i;
// виведення масиву за допомогою циклу for
for (i=0; i<tab.length; i++)
System.out.println("tab[" + i + "]=" + tab[i]);
// генерація винятку
try{
tab[100]=6;
}catch (Exception e){
System.err.println("L'erreur suivante s'est produite : " + e);
}//try-catch
}//main
}//клас
Виконання програми дає такі результати:
tab[0]=0
tab[1]=1
tab[2]=2
tab[3]=3
L'erreur suivante s'est produite : java.lang.ArrayIndexOutOfBoundsException
Ось ще один приклад, де обробляється виняток, спричинений присвоєнням рядка символів числу, коли цей рядок не є числом:
// імпорт
import java.io.*;
public class console1{
public static void main(String[] args){
// створення потоку введення
BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
}catch(Exception ex){}
// Запитується ім'я
System.out.print("Nom : ");
// читання відповіді
String nom=null;
try{
nom=IN.readLine();
}catch(Exception ex){}
// запитується вік
int age=0;
boolean ageOK=false;
while ( ! ageOK){
// запитання
System.out.print("âge : ");
// читання та перевірка відповіді
try{
age=Integer.parseInt(IN.readLine());
ageOK=true;
}catch(Exception ex) {
System.err.println("Age incorrect, recommencez...");
}//try-catch
}//цикл while
// остаточне відображення
System.out.println("Vous vous appelez " + nom + " et vous avez " + age + " ans");
}//Main
}//клас
Деякі результати виконання:
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. Компіляція та виконання програми на Java
Скомпілюйте, а потім виконайте таку програму:
// імпорт класів
import java.io.*;
// тестовий клас
public class coucou{
// функція main
public static void main(String args[]){
// виведення на екран
System.out.println("coucou");
}//main
}//клас
Вихідний файл, що містить попередній клас coucou, обов’язково має називатися coucou.java:
Компіляція та виконання програми Java відбувається у вікні DOS. Виконувані файли javac.exe (компілятор) та java.exe (інтерпретатор) знаходяться у каталозі bin, що входить до складу каталогу встановлення 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
Компілятор javac.exe аналізує вихідний файл .java та створює скомпільований файл .class. Цей файл не може бути негайно виконаний процесором. Для цього потрібен інтерпретатор Java (java.exe), який називають віртуальною машиною або JVM (Java Virtual Machine). На основі проміжного коду, що міститься у файлі .class, віртуальна машина генерує інструкції, специфічні для процесора комп’ютера, на якому вона виконується. Існують віртуальні машини Java для різних типів операційних систем (Windows, Unix, Mac OS тощо). Файл .class можна виконати на будь-якій із цих віртуальних машин, а отже, на будь-якій операційній системі. Ця міжсистемна переносимість є однією з головних переваг Java.
Скомпілюємо попередню програму:
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
Запустимо створений файл .class:
Слід зауважити, що у наведеному вище запиті на виконання не вказано суфікс .class файлу coucou.class, який потрібно виконати. Він є неявним. Якщо каталог bin, що належить до файлу JDK, знаходиться в каталозі PATH на машині DOS, можна не вказувати повний шлях до виконуваних файлів javac.exe та java.exe. У такому разі достатньо просто написати
2.8. Аргументи головної програми
Головна функція main приймає як параметри масив рядків: String[]. Цей масив містить аргументи командного рядка, що використовується для запуску програми. Отже, якщо запустити програму P за допомогою команди:
і якщо функція main оголошена наступним чином:
то отримаємо arg[0]="arg0", arg[1]="arg1" … Ось приклад:
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]);
}
}
Отримано такі результати:
2.9. Передача параметрів у функцію
У попередніх прикладах були показані лише програми на Java, що містять лише одну функцію — головну функцію main. Наступний приклад демонструє, як використовувати функції та як відбувається обмін інформацією між ними. Параметри функції завжди передаються за значенням: тобто значення фактичного параметра копіюється у відповідний формальний параметр.
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);
}
}
Отримано такі результати:
Значення фактичних параметрів «papa» та 20 були скопійовані у формальні параметри S та a. Потім ці параметри були змінені. Фактичні параметри залишилися без змін. Слід звернути увагу на тип фактичних параметрів:
- S — це посилання на об’єкт c.a.d, тобто адреса об’єкта в пам’яті
- age — це ціле значення
2.10. Приклад з податками
Завершимо цей розділ прикладом, до якого ми будемо неодноразово повертатися в цьому документі. Ми поставимо собі за мету написати програму, яка дозволить обчислити податок платника податків. Розглянемо спрощений випадок, коли платник податків має задекларувати лише свою заробітну плату:
- розраховуємо кількість часток працівника nbParts=nbEnfants/2 +1, якщо він не одружений, nbEnfants/2+2, якщо він одружений, де nbEnfants — кількість його дітей.
- якщо у нього щонайменше троє дітей, він отримує на півчастини більше
- розраховується його оподатковуваний дохід R = 0,72 * S, де S — його річна заробітна плата
- розраховується його сімейний коефіцієнт QF = R / nbParts
- розраховується його податок I. Розглянемо таку таблицю:
12620,0 | 0 | 0 |
13190 | 0,05 | 631 |
15640 | 0,1 | 1290,5 |
24740 | 0,15 | 2072,5 |
31810 | 0,2 | 3309,5 |
39970 | 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 |
172040 | 0,55 | 30710 |
195 000 | 0,60 | 39312 |
0 | 0,65 | 49062 |
Кожен рядок має 3 поля. Щоб обчислити податок I, шукаємо перший рядок, у якому QF <= поле1. Наприклад, якщо QF = 23000, то знайдемо рядок
Тоді податок I дорівнює 0,15*R - 2072,5*nbParts. Якщо QF є таким, що співвідношення QF<=поле1 ніколи не виконується, то використовуються коефіцієнти останнього рядка. У даному випадку:
що дає податок I=0,65*R - 49062*nbParts.
Відповідна програма на Java виглядає так:
import java.io.*;
public class impots{
// ------------ main
public static void main(String arg[]){
// дані
// межі податкових груп
double Limites[]={12620, 13190, 15640, 24740, 31810, 39970, 48360,55790, 92970, 127860, 151250, 172040, 195000, 0};
// коефіцієнт, що застосовується до кількості часток
double Coeffn[]={0, 631, 1290.5, 2072.5, 3309.5, 4900, 6898.5, 9316.5,12106, 16754.5, 23147.5, 30710, 39312, 49062};
// програма
// створення потоку введення з клавіатури
BufferedReader IN=null;
try{
IN=new BufferedReader(new InputStreamReader(System.in));
}
catch(Exception e){
erreur("Création du flux d'entrée", e, 1);
}
// отримання сімейного стану
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");
// кількість дітей
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
// зарплата
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
// розрахунок кількості часток
double NbParts;
if(Marie) NbParts=(double)NbEnfants/2+2;
else NbParts=(double)NbEnfants/2+1;
if (NbEnfants>=3) NbParts+=0.5;
// оподатковуваний дохід
double Revenu;
Revenu=0.72*Salaire;
// сімейний коефіцієнт
double QF;
QF=Revenu/NbParts;
// пошук податкової групи, що відповідає QF
int i;
int NbTranches=Limites.length;
Limites[NbTranches-1]=QF;
i=0;
while(QF>Limites[i]) i++;
// податок
long impots=(long)(i*0.05*Revenu-Coeffn[i]*NbParts);
// виводиться результат
System.out.println("Impôt à payer : " + impots);
}// головне меню
// ------------ помилка
private static void erreur(String msg, Exception e, int exitCode){
System.err.println(msg+"("+e+")");
System.exit(exitCode);
}// помилка
}// клас
Отримано такі результати:
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