|
Лексика мови
ADO в Delphi AJAX Android C++ CakePHP CMS COM CSS Delphi Flash Flex HTML Internet Java JavaScript MySQL PHP RIA SCORM Silverlight SQL UML XML Бази даних Веб-розробка Генетичні алгоритми ГІС Гітара Дизайн Економіка Інтелектуальні СДН Колір Масаж Математика Медицина Музика Нечітка логіка ООП Патерни Подання знань Розкрутка сайту, SEO САПР Сесії в PHP Системне програмування Системний аналіз Тестологія Тестування ПЗ Фреймворки Штучний інтелект
|
Лексика мови
КодуванняТехнологія Java, як платформа, спочатку спроектована для Глобальної мережі Internet, повинна бути багатомовною, а значить, звичайний набір символів ASCII (American Standard Code for Information Interchange, Американський стандартний код обміну інформацією), що включає в себе лише латинський алфавіт, цифри і найпростіші спеціальні знаки (дужки, знаки пунктуації, арифметичні операції і т.д.), недостатній. Тому для запису тексту програми застосовується більш універсальне кодування Unicode. Як відомо, Unicode представляє символи кодом з 2 байт, описуючи, таким чином, 65535 символів. Це дозволяє підтримувати практично всі поширені мови світу. Перші 128 символів збігаються з набором ASCII. Однак зрозуміло, що потрібен якийсь спеціальне позначення, щоб мати можливість задавати в програмі будь-який символ Unicode, адже ніяка клавіатура не дозволяє вводити більше 65 тисяч різних знаків. Ця конструкція представляє символ Unicode, використовуючи тільки символи ASCII. Наприклад, якщо в програму потрібно вставити знак з кодом 6917, необхідно його представити в шістнадцятковому форматі (1B05) і записати u1B05, причому літера u повинна бути малої, а шістнадцяткові цифри A, B, C, D, E, F можна використовувати довільно, як великі, так і малі. Таким чином можна закодувати всі символи Unicode від \ u0000 до \ uFFFF. Літери російського алфавіту починаються з \ u0410 (тільки буква Е має код \ u0401) по \ u044F (код літери е \ u0451). В останніх версіях JDK до складу демонстраційних додатків і аплетів входить невелика програма SymbolTest, що дозволяє переглядати весь набір символів Unicode. Її аналог нескладно написати самостійно. Для перекодування великих текстів служить утиліта native2ascii, що також входить в JDK. Вона може працювати як в прямому режимі - переводити з різноманітних кодувань в Unicode, записаний ASCII-символами, так і в зворотному (опція-reverse) - з Unicode в стандартну кодування операційної системи. У версіях мови Java до 1.1 застосовувався Unicode версії 1.1.5, в останньому випуску 1.4 використовується 3.0. Таким чином, Java стежить за розвитком стандарту і базується на сучасних версіях. Для будь-якої JDK точну версію Unicode, використовувану в ній, можна дізнатися з документації до класу Character. Офіційний web-сайт стандарту, де можна отримати додаткову інформацію, - http://www.unicode.org/. Отже, використовуючи найпростішу кодування ASCII, можна ввести довільну послідовність символів Unicode. Далі буде показано, що Unicode використовується не для всіх лексем, а тільки для тих, для яких важлива підтримка багатьох мов, а саме: коментарі, ідентифікатори, символьні і рядкові літерали. Для запису інших лексем цілком достатньо ASCII-символів. Аналіз програмиКомпілятор, аналізуючи програму, відразу поділяє його на:
ПробілиПробілами в даному випадку називають всі символи, що розбивають текст програми на лексеми. Це як сам символ пробілу (space, \ u0020, десятковий код 32), так і знаки табуляції та переведення рядка. Вони використовуються для розділення лексем, а також для оформлення коду, щоб його було легше читати. Наприклад, наступну частину програми (обчислення коренів квадратного рівняння):
можна записати і в такому вигляді:
double a = 1, b = 1, c = 6; double D = b * b-4 * a * c; if (D> = 0)
{Double x1 = (-b + Math.sqrt (D)) / (2 * a); double
x2 = (-b-Math.sqrt (D)) / (2 * a);}
В обох випадках компілятор згенерує абсолютно однаковий код. Єдине міркування, яким повинен керуватися розробник, - легкість читання та подальшої підтримки такого коду. Для розбиття тексту на рядки в ASCII використовується два символи - "повернення каретки" (carriage return, CR, \ u000d, десятковий код 13) і символ нового рядка (linefeed, LF, \ u000a, десятковий код 10). Щоб не залежати від особливостей використовуваної платформи, в Java застосовується найбільш гнучкий підхід. Завершенням рядка вважається:
Розбиття на рядки важливо для коректного розбиття на лексеми (як вже говорилося, завершення рядка також служить роздільником між лексемами), для правильної роботи з рядковими коментарями (див. наступну тему "Коментарі"), а також для виведення налагоджувальної інформації (при виведенні помилок компіляції і часу виконання вказується, на якому рядку вихідного коду вони виникли). Отже, пробілами в Java вважаються:
КоментаріКоментарі не впливають на результуючий бінарний код і використовуються тільки для введення пояснень до програми. У Java коментарі бувають двох видів:
Рядкові коментарі починаються з ASCII-символів int y = 1970; // рік народження
Блокові коментарі розташовуються між ASCII-символами / * Цей цикл не може починатися з нуля через особливості алгоритму * / for (int i = 1; i <10; i + +) { ... }
Часто блокові коментарі оформляють таким чином (кожний рядок починається з
/ *
* Опис алгоритму роботи
* Наступного циклу while
* /
while (x> 0) {
...
}
Блоковий коментар не обов'язково повинен розташовуватися на декількох рядках, він може навіть перебувати в середині оператора:
float s = 2 * Math.PI / * getRadius ()*/;
// Закоментований для налагодження
У цьому прикладі блочний коментар розбиває арифметичні операції. Вираз Math.PI надає значення константи PI, визначене в класі Math. Виклик методу Коментарі не можуть перебувати в символьних і рядкових літералах, ідентифікаторах (ці поняття докладно розглядаються далі в цій лекції). Наступний приклад містить випадки неправильного застосування коментарів:
// У цьому прикладі текст / * ... * / стане просто
// Частиною рядка s
String s = "text / * just text * /";
/ *
Наступний рядок стане причиною помилки
при компіляції, так як коментар розбив
ім'я методу getRadius ()
* /
circle.get / * comment * / Radius ();
А такий код можливий: // Коментар може розділяти виклики функцій: circle. / * comment * / getRadius (); // Коментар може замінювати пробіли: int / * comment * / x = 1; В останньому рядку між назвою типу даних int і назвою змінної x обов'язково повинен бути пробіл або, як в даному прикладі, коментар.
Коментарі не можуть бути вкладеними. Символи
/ * Початок коментаря / * / // ** завершення: * /
описаний тільки один блоковий коментар. А в наступному прикладі (рядки коду пронумеровані для зручності) 1. /* 2. comment 3. /* 4. more comments 5. */ 6. finish 7. */ компілятор видасть помилку. Блоковий коментар почався в рядку 1 з комбінації символів / *. Друга відкриває комбінація / * на рядку 3 буде проігнорована, так як знаходиться вже всередині коментаря. Символи * / у рядку 5 завершать його, а рядок 7 породить помилку - спроба закрити коментар, який не був початий. Будь-які коментарі повністю видаляються з програми під час компіляції, тому їх можна використовувати необмежено, не побоюючись, що це вплине на бінарний код. Основне їх призначення - зробити програму простою для розуміння, в тому числі і для інших розробників, яким доведеться в ній розбиратися з якої-небудь причини. Також коментарі найчастіше використовуються для тимчасового виключення частин коду, наприклад:
int x = 2;
int y = 0;
/*
if (x> 0)
y = y + x * 2;
else
y =-y - x * 4;
*/
y = y * y; // + 2 * x;
У цьому прикладі закоментований вираз i Як вже говорилося вище, коментарі можна писати символами Unicode, тобто будь-якою мовою, зручному розробнику. Крім цього, існує особливий вид блокового коментарю - коментар розробника. Він застосовується для автоматичного створення документації коду. У стандартну поставку JDK, починаючи з версії 1.0, входить спеціальна утиліта javadoc. На вхід їй подається вихідний код класів, а на виході виходить зручна документація в HTML-форматі, яка описує всі класи, всі їх поля і методи. При цьому активно використовуються гіперпосилання, що істотно спрощує вивчення програми (наприклад, читаючи опис методу, можна за допомогою одного натискання миші перейти на опис типів, що використовуються в якості аргументів чи значення, що повертається). Однак зрозуміло, що однієї назви методу і перерахування його аргументів недостатньо для розуміння його роботи. Необхідні додаткові пояснення від розробника.
Коментар розробника записується так само, як і блоковий. Єдина відмінність в початковій комбінації символів - для документації коментар необхідно починати з
/**
* Обчислення модуля цілого числа.
* Цей метод повертає
* Абсолютне значення аргументу x.
*/
int getAbs (int x) {
if (x> = 0)
return x;
else
return-x;
}
Перше речення повинне містити коротке резюме всього коментаря. Надалі воно буде використано як пояснення цієї функції у списку всіх методів класу (нижче будуть описані всі конструкції мови, для яких застосовується коментар розробника).
Оскільки в результаті створюється HTML-документація, то і коментар необхідно писати за правилами HTML. Допускається застосування тегів, таких як Символ * на початку кожного рядка і попередні пробіли та знаки табуляції ігноруються. Їх можна не використовувати взагалі, але вони зручні, коли необхідно форматування, скажімо, в прикладах коду. /** * Перша речення - короткий * Опис методу. * <p> * Так оформляється приклад коду: * <blockquote> * <pre> * If (condition == true) { * X = getWidth (); * Y = x.getHeight (); *} * </ Pre> </ blockquote> * А так описується HTML-список: * <ul> * <li> Можна використовувати похилий шрифт * <i> Курсив </ i>, * <li> Або жирний <b> жирний </ b>. * </ Ul> * / public void calculate (int x, int y) { ... } З цього коментаря буде згенерований HTML-код, який виглядає приблизно так: Перша пропозиція - короткий опис методу. Так оформляється приклад коду:
if (condition == true) {
x = getWidth ();
y = x.getHeight ();
}
А так описується HTML-список:
Нарешті, javadoc підтримує спеціальні теги. Вони починаються з символу @. Детальний опис цих тегів можна знайти в документації. Наприклад, можна використовувати тег @ see, щоб послатися на інший клас, поле або метод, або навіть на іншій Internet-сайт.
/**
* Короткий опис.
*
* Розгорнутий коментар.
*
* @ See java.lang.String
* @ See java.lang.Math # PI
* @ See <a href="java.sun.com"> Official
* Java site </ a>
*/
Перше посилання вказує на клас String (java.lang - назва бібліотеки, в якій знаходиться цей клас), друга - на поле PI класу Math (символ # розділяє назву класу і його полів або методів), третя посилається на офіційний сайт Java. Коментарі розробника може бути записані перед оголошенням класів, інтерфейсів, полів, методів і конструкторів. Якщо записати коментар / ** ... * / в іншій частині коду, то помилки не буде, але він не потрапить в документацію, що генерується javadoc. Крім того, можна описати пакет (так називаються бібліотеки, або модулі, в Java). Для цього необхідно створити спеціальний файл package.html, зберегти в ньому коментар і помістити його в каталог пакета. HTML-текст, що міститься між тегами <body> і </ body>, буде поміщений в документацію, а перше речення буде використовуватися для короткої характеристики цього пакета. ЛексемиОтже, ми розглянули пробіли (в широкому сенсі цього слова, тобто всі символи, що відповідають за форматування тексту програми) та коментарі, що застосовуються для введення пояснень до коду. З точки зору програміста вони застосовуються для того, щоб зробити програму більш читабельною і зрозумілою для подальшого розвитку. З точки зору компілятора, а точніше його частини, що відповідає за лексичний розбір, основна роль пропусків і коментарів - служити роздільниками між лексемами, причому самі роздільники далі відкидаються і на скомпільований код не впливають. Наприклад, всі наступні приклади оголошення змінної еквівалентні:
// Використовуємо пробіл як роздільник.
int x = 3;
// Тут роздільником є переведення рядка
int
x
=
3
;
// Тут поділяємо знаком табуляції
int x = 3;
/*
* Єдиний принципово необхідний
* Роздільник між назвою типу даних
* Int і ім'ям змінної x тут описаний
* Коментарем блокового типу.
*/
int /**/ x = 3;
Звичайно, лексеми дуже різноманітні, і саме вони визначають багато властивостей мови. Розглянемо всі їх види більш детально. Види лексемНижче перераховані всі види лексем в Java:
Розглянемо їх окремо. ІдентифікаториІдентифікатори - це імена, які даються різним елементам мови для спрощення доступу до них. Імена мають пакети, класи, інтерфейси, поля, методи, аргументи і локальні змінні (усі ці поняття докладно розглядаються в наступних лекціях). Ідентифікатори можна записувати символами Unicode, тобто на будь-якому зручному мовою. Довжина імені не обмежена.
Ідентифікатор складається з букв і цифр. Ім'я не може починатися з цифри. Java-букви, що використовуються в ідентифікаторах, включають в себе ASCII-символи Для ідентифікаторів не допускаються збіги з зарезервованими словами (це ключові слова, булевські літерали true і false і null-літерал null). Звичайно, якщо 2 ідентифікатора включають в себе різні літери, які однаково виглядають (наприклад, латинська і російська букви A), то вони вважаються різними. У цій лекції вже застосовувалися наступні ідентифікатори:
Також допустимими є ідентифікатори:
Ключові словаКлючові слова - це зарезервовані слова, що складаються з ASCII-символів і виконують різні завдання мови. Ось їх повний список (48 слів): abstract double int strictfp boolean else interface super break extends long switch byte final native synchronized case finally new this catch float package throw char for private throws class goto protected transient const if public try continue implements return void default import short volatile do instanceof static while Ключові слова goto і const зарезервовані, але не використовуються. Це зроблено для того, щоб компілятор міг правильно відреагувати на їх використання в інших мовах. Навпаки, обидва булевских літерала true, false і null-літерал null часто вважають ключовими словами (можливо, тому, що багато засобів розробки підсвічують їх таким же чином), проте це саме літерали. Значення всіх ключових слів буде розглядатися в наступних лекціях. ЛітералиЛітерали дозволяють задати в програмі значення для числових, символьних і рядкових виразів, а також null-літералов.Всього в Java визначено 6 видів літералів:
Розглянемо їх окремо. Цілочисельні літералиЦілочисельні літерали дозволяють задавати цілочисельні значення в десятковому, вісімковому і шістнадцятковому вигляді. Десятерічний формат традиційний і нічим не відрізняється від правил, прийнятих в інших мовах. Значення в вісімковому вигляді починаються з нуля, і, звичайно, використання цифр 8 і 9 заборонено. Запис шістнадцяткових чисел починається з 0x або 0X (цифра 0 і латинська ASCII-буква X в довільному регістрі). Таким чином, нуль можна записати трьома різними способами: 0 00 0x0 Як звичайно, для запису цифр 10-15 в шістнадцятковому форматі використовуються букви A, B, C, D, E, F, прописні або рядкові. Приклади таких літералів: 0xaBcDeF, 0xCafe, 0xDEC Типи даних розглядаються нижче, однак тут необхідно згадати два цілочисельних типи int і long довжиною 4 і 8 байт, відповідно (або 32 і 64 біта, відповідно). Обидва ці типи знакові, тобто тип int зберігає значення від -231 до 231-1, або від -2.147.483.648 до 2.147.483.647. За замовчуванням цілочисельний літерал має тип int, а значить, у програмі допустимо використовувати літерали тільки від 0 до 2147483648, інакше виникне помилка компіляції. При цьому літерал 2147483648 можна використовувати тільки як аргумент унарні оператора -:
int x = -2147483648; \\ вірно
int y = 5-2147483648; \\ тут виникне помилка компіляції
Відповідно, допустимі літерали в вісімковій записи повинні бути від 00 до 017 777 777 777 (= 231-1), з унарним оператором - припустимо також -020000000000 (= -231). Аналогічно для шістнадцяткового формату - від 0x0 до 0x7fffffff (= 231-1), а також-0x80000000 (= -231). Тип long має довжину 64 біта, а значить, дозволяє зберігати значення від -263 до 263-1. Щоб ввести такий літерал, необхідно в кінці поставити латинську букву L або l, тоді все значення буде трактуватися як long. Аналогічно можна виписати максимальні допустимі значення для них:
9223372036854775807L
0777777777777777777777L
0x7fffffffffffffffL
// Найбільші негативні значення:
-9223372036854775808L
-01000000000000000000000L
-0x8000000000000000L
Інші приклади цілочисельних літералів типу long: 0L, 123l, 0xC0B0L Дробові літерали Дробові літерали представляють собою числа з плаваючою десятковою крапкою. Правила запису таких чисел такі ж, як і в більшості сучасних мов програмування. Приклади:
3.14
2.
.5
7e10
3.1E-20
Таким чином, дробовий літерал складається з наступних складових частин:
Ціла та дробова частини записуються десятковими цифрами, а вказівник типу (аналог покажчика L або l для цілочисельних літералів типу long) має два можливих значення - латинська ASCII-буква D (для типу double) або F (для типу float) в довільному регістрі. Вони будуть детально розглянуті нижче. Необхідними частинами є:
Всі інші частини необов'язкові. Таким чином, "мінімальні" дробові літерали можуть бути записані, наприклад, так:
1.
.1
1e1
1f
У Java є два дробових типу, згадані вище, - float і double. Їх довжина - 4 і 8 байт або 32 і 64 біта, відповідно. Дробний літерал має тип float, якщо він закінчується на латинську букву F в довільному регістрі. В іншому випадку він розглядається як значення типу double і може включати в себе закінчення D або d, як ознака типу double (використовується тільки для наочності).
// Float-літерали:
1f, 3.14F, 0f, 1e +5 F
// Double-літерали:
0., 3.14d, 1e-4, 31.34E45D
У Java дробові числа 32-бітного типу float і 64-бітного типу double зберігаються в пам'яті в бінарному вигляді в форматі, стандартизованому специфікацією IEEE 754 (повна назва - IEEE Standard for Binary Floating-Point Arithmetic, ANSI / IEEE Standard 754-1985 (IEEE , New York)). У цій специфікації описані не тільки кінцеві дробові величини, але й ще кілька особливих значень, а саме:
Для цих значень немає спеціальних позначень. Щоб отримати такі величини, необхідно або провести арифметичну операцію (наприклад, результатом поділу нуль на нуль 0.0/0.0 є NaN), або звернутися до констант в класах Float і Double, а саме POSITIVE_INFINITY, NEGATIVE_INFINITY і NaN. Більш докладно робота з цими особливими значеннями розглядається в наступній лекції. Типи даних накладають обмеження на можливі значення літералів, як і для цілочисельних типів. Максимальне позитивне кінцеве значення дробового літерала:
Крім того, для дробових величин стає важливим ще одне граничне значення - мінімальне позитивне ненульове значення:
Спроба вказати літерал із занадто великим абсолютним значенням (наприклад, 1e40F) призведе до помилки компіляції. Така величина повинна представлятися нескінченністю. Аналогічно, вказівку літерала із занадто малим ненульовим значенням (наприклад, 1e-350) також приводить до помилки. Це значення повинно бути округлене до нуля. Однак якщо округлення призводить не до нуля, то компілятор зробить його сам:
// Помилка, вираз має бути округлена до 0
0.00000000000000000000000000000000000000000001f
// Помилки немає, компілятор сам округлює до 1
1.00000000000000000000000000000000000000000001f
Стандартних можливостей вводити дробові значення не в десятковій системі в Java немає, проте класи Float і Double надають багато допоміжних методів, в тому числі і для такого завдання.
Логічні літерали
Логічні літерали мають два можливих значення - true і false. Ці два зарезервованих слова не є ключовими, але також не можуть використовуватися в якості ідентифікатора. Символьні літералиСимвольні літерали описують один символ з набору Unicode, укладений в одиночні лапки, або апострофи (ASCII-символ single quote, \ u0027). Наприклад:
'A' // латинська літера а
'' // Пробіл
'K' // грецька буква каппа
Також допускається спеціальний запис для опису символу через його код (див. тему "Кодування"). Приклади:
'\ U0041' // латинська буква A
'\ U0410' // російська буква А
'\ U0391' // грецька буква A
Символьний літерал повинен містити строго один символ, або спеціальну послідовність, що починається з \. Для запису спеціальних символів (невідображених і службових, таких як ", ', \) використовуються наступні позначення: \ B \ u0008 backspace BS - забій \ T \ u0009 horizontal tab HT - табуляція \ N \ u000a linefeed LF - кінець рядка \ F \ u000c form feed FF - кінець сторінки \ R \ u000d carriage return CR -
\"\ U0022 double quote" - лапки \'\ U0027 single quote' - одинарна лапка \\\ U005c backslash \ - зворотна коса риска \ Шістнадцятковий код від \ u0000 до \ u00ff символу
Перша колонка описує стандартні позначення спеціальних символів, які використовуються в Java-програмах. Друга колонка представляє їх в стандартному вигляді Unicode-символів. Третя колонка містить англійські і російські опису. Використання \ в комбінації з іншими символами призведе до помилки компіляції. Підтримка введення символів через вісімковий код забезпечується для сумісності з С. Наприклад:
'\ 101' // Еквівалентно '\ u0041'
Проте таким чином можна поставити лише символи від \ u0000 до \ u00ff (тобто з кодом від 0 до 255), тому Unicode-послідовності краще. Оскільки обробка Unicode-послідовностей (\ uhhhh) проводиться раніше лексичного аналізу, то наступний приклад є помилкою: '\ U000a' // символ кінця рядка Компілятор спочатку перетворить \ u000a на символ кінця рядка і лапки опиняться на різних рядках коду, що є помилкою. Необхідно використовувати спеціальну послідовність: '\ N' / правильне позначення кінця рядка Аналогічно і для символу \ u000d (повернення каретки) необхідно використовувати позначення \ r. Спеціальні символи можна використовувати у складі як символьних, так і рядкових літералів. Стрічкові літералиСтрічкові літерали складаються з набору символів і записуються в подвійних лапках. Довжина може бути нульовою або скільки завгодно великою. Будь-який символ може бути представлений спеціальною послідовністю, що починається з \ (див. "Символьні літерали"). "" // Літерал нульової довжини "\" "// Літерал, що складається з одного символу" "Простий текст" // літерал довжини 13 Стрічковий літерал не можна розбивати на кілька рядків у коді програми. Якщо потрібно текстове значення, що складається з декількох рядків, то необхідно скористатися спеціальними символами \ n і / або \ r. Якщо ж текст просто занадто довгий, щоб вміститися на одному рядку коду, можна використовувати оператор конкатенації рядків +. Приклади строкових літералів:
// Вираз-константа, складений з двох
// Літералів
"Довгий текст" +
"З перенесенням"
/*
* Стрічковий літерал, що містить текст
* З двох рядків:
* Hello, world!
* Hello!
* /
"Hello, world! \R\ nHello!"
На рядкові літерали поширюються ті ж правила, що і на символьні щодо використання символів нового рядка \ u000a і \ u000d. Кожен строковий літерал є екземпляром класу String. Це визначає деякі незвичайні властивості строкових літералів, які будуть розглянуті в наступній лекції. Null-літералNull-літерал може приймати всього одне значення: null. Це літерал вказівного типу, причому це посилання нікуди не посилається, об'єкт відсутній. Зрозуміло, його можна застосовувати до посилань будь-якого об'єктного типу даних. Типи даних докладно розглядаються в наступній лекції. РозділювачіРоздільники - це спеціальні символи, які використовуються в службових цілях мови. Призначення кожного з них буде розглянуто по ходу викладу курсу. Ось їх повний список:
() [] {};.,
ОператориОператори використовуються в різних операціях - арифметичних, логічних, бітових, операціях порівняння і присвоювання. Наступні 37 лексем (усі складаються тільки з ASCII-символів) є операторами мови Java: => <! ~? : == <=> =! = & & | | + + - + - * / & | ^% <<>>>>> + = -= *= / = & = | = ^ =% = <<=>> =>>> = Більшість з них цілком очевидні і добре відомі з інших мов програмування, проте деякі нюанси в роботі з операторами в Java все ж таки присутні, тому наприкінці лекції наводяться короткі коментарі до них. Приклад програми На закінчення для прикладу наведемо просту програму (традиційне Hello, world!), А потім класифікуємо і підрахуємо використовувані лексеми:
public class Demo {
/**
* Основний метод, з якого починається
* Виконання будь-Java програми.
*/
public static void main (String args [])
{
System.out.println ("Hello, world!");
}
}
Отже, у наведеній програмі є один коментар розробника, 7 ідентифікаторів, 5 ключових слів, 1 строковий літерал, 13 роздільників і жодного оператора. Цей текст можна зберегти у файлі Demo.java, скомпілювати і запустити. Результатом роботи буде, як очевидно:
Hello, world!
Доповнення. Робота з операторамиРозглянемо деякі деталі використання операторів в Java. Тут будуть описані подробиці, що стосуються роботи самих операторів. У наступній лекції детально розглядаються особливості, що виникають при використанні різних типів даних (наприклад, значення операції 1 / 2 дорівнює 0, а 1 / 2. Одно 0.5). Оператори присвоювання і порівнянняПо-перше, звичайно ж, різняться оператор присвоювання = і оператор порівняння ==.
x = 1; // присвоюємо змінної x значення 1
x == 1 // порівнюємо значення змінної x з одиницею
Оператор порівняння завжди повертає булеве значення true або false. Оператор присвоювання повертає значення правого операнда. Тому звичайна помилка в мові С, коли ці оператори плутають:
// Приклад викличе помилку компілятора
if (x = 0) {// тут повинен застосовуватися оператор порівняння ==
...
}
в Java легко усувається. Оскільки вираз x = 0 має числове значення 0, а не булеве (і тим більше не сприймається як завжди істинне), то компілятор повідомляє про помилку (необхідно писати x == 0). Умова "не дорівнює" записується як! =. Наприклад:
if (x! = 0) {
float f = 1. / x;
}
Поєднання будь-якого оператора з оператором присвоєння = (див. нижній рядок в повному переліку в розділі "Оператори") використовується при зміні значення змінної. Наприклад, наступні два рядки еквівалентні:
x = x + 1;
x + = 1;
Арифметичні операціїПоряд з чотирма звичайними арифметичними операціями +, -, *, /, існує оператор отримання залишку від ділення %, який може бути застосований як до цілочисловим аргументів, так і до дробовим. Робота з цілочисельними аргументами підкоряється простим правилам. Якщо ділиться значення a на значення b, то вираз (a / b) * b + (a% b) повинна в точності дорівнювати a. Тут, звичайно, оператор ділення цілих чисел / завжди повертає ціле число. Наприклад:
9 / 5 повертає 1
9 / (-5) повертає -1
(-9) / 5 повертає -1
(-9) / (-5) Повертає 1
Залишок може бути позитивним, тільки якщо ділене було позитивним. Відповідно, залишок може бути негативним тільки в разі негативного діленого.
9% 5 повертає 4
9% (-5) повертає 4
(-9)% 5 повертає -4
(-9)% (-5) Повертає -4
Спроба отримати залишок від ділення на 0 призводить до помилки. Ділення з залишком для дробових чисел може бути вироблено за двома різними алгоритмами. Один з них повторює правила для цілих чисел, і саме він представлений оператором%. Якщо в розглянутому прикладі поділу 9 на 5 перейти до дробовим числах, значення залишку у всіх варіантах не зміниться (воно буде також дробовим, звичайно).
9.0% 5.0 повертає 4.0
9.0% (-5.0) повертає 4.0
(-9.0)% 5.0 повертає -4.0
(-9.0)% (-5.0) Повертає -4.0
Однак стандарт IEEE 754 визначає інші правила. Такий спосіб представлений методом стандартного класу Math.IEEEremainder (double f1, double f2). Результат цього методу - значення, яке дорівнює f1-f2 * n, де n - ціле число, найближчим до значення f1/f2, а якщо два цілих числа однаково близькі до цього відношення, то вибирається парне. За цим правилом значення залишку буде іншим:
Math.IEEEremainder (9.0, 5.0) повертає -1.0
Math.IEEEremainder (9.0, -5.0) повертає -1.0
Math.IEEEremainder (-9.0, 5.0) повертає 1.0
Math.IEEEremainder (-9.0, -5.0) повертає 1.0
Унарні оператори інкрементаціі + + і декрементаціі -, як завжди, можна використовувати як справа, так і зліва.
int x = 1;
int y = + + x;
У цьому прикладі оператор + + стоїть перед змінної x, це означає, що спочатку відбудеться інкрементація, а потім значення x буде використано для ініціалізації y. У результаті після виконання цих рядків значення x і y будуть рівні 2.
int x = 1;
int y = x + +;
А в цьому прикладі спочатку значення x буде використано для ініціалізації y, і лише потім відбудеться інкрементація. У результаті значення x буде дорівнює 2, а y буде дорівнювати 1. Логічні операториЛогічні оператори "і" та "або" (& і |) можна використовувати у двох варіантах. Це пов'язано з тим, що, як легко переконатися, для кожного оператора можливі випадки, коли значення першого операнда відразу визначає значення всього логічного виразу. Якщо другим операндом є значення деякої функції, то з'являється вибір - викликати її чи ні, причому це рішення може позначитися як на швидкості, так і на функціональності програми. Перший варіант операторів (&, |) завжди обчислює обидва операнда, другий же - (& &, | |) не буде продовжувати обчислення, якщо значення виразу вже очевидно. Наприклад:
int x = 1;
(X> 0) | calculate (x) // в такому виразі відбудеться виклик Calculate
(X> 0) | | calculate (x) / / а в цьому - ні
Логічний оператор заперечення "не" записується як! і, звичайно, має тільки один варіант використання. Цей оператор змінює булеве значення на протилежне.
int x = 1;
x> 0 / / вираз істинний
! (X> 0) / / вираз хибний
Оператор з умовою ? : Складається з трьох частин - умови і двох виразів. Спочатку обчислюється умова (булеве вираз), а на підставі результату значення всього оператора визначається першим виразом у разі отримання істини і другим - якщо умова хибна. Наприклад, так можна обчислити модуль числа x:
x> 0? x:-x
Бітові операціїПерш ніж переходити до двійкового операціями, необхідно уточнити, яким саме чином цілі числа представляються в двійковому вигляді. Звичайно, для невід'ємних величин це практично очевидно:
0 0
1 січня
Лютого 1910
11 березня
4 100
5 101
і так далі. Однак як представляються негативні числа? По-перше, вводять поняття знакового біта. Перший біт починає відповідати за знак, а саме 0 означає позитивне число, 1 - негативне. Але не слід думати, що інші біти залишаються незмінними. Наприклад, якщо розглянути 8-бітове уявлення:
-1 Один мільйон тисячі / / це НЕВІРНО!
-2 10000010 / / це НЕВІРНО!
-3 10000011 / / це НЕВІРНО!
Такий підхід є невірним! Зокрема, ми отримуємо відразу два подання нуля - 00000000 і 100000000, що нераціонально. Правильний алгоритм можна уявити собі так. Щоб отримати значення -1, треба з 0 відняти 1:
00000000
- 00000001
------------
- 11111111
Отже, -1 у двійковому вигляді представляється як 11111111. Продовжуємо застосовувати той же алгоритм (віднімаємо 1):
0 00000000
-1 11111111
-2 11111110
-3 11111101
і так далі до значення 10000000, яке являє собою найбільшу за модулем від'ємне число. Для 8-бітового уявлення найбільший позитивний число 01111111 (= 127), а найменший негативний 10000000 (=- 128). Оскільки всього 8 біт визначає 28 = 256 значень, причому одне з них відводиться для нуля, то стає ясно, чому найбільші за модулем позитивні і негативні значення розрізняються на одиницю, а не збігаються. Як відомо, бітові операції "і", "або", "виключає або" беруть два аргументи і виконують логічну дію попарно над відповідними бітами аргументів. При цьому використовуються ті ж позначення, що і для логічних операторів, але, звичайно, тільки в першому (одиночному) варіанті. Наприклад, обчислимо вираз 5 & 6:
00000101
& 00000110
-------------
00000100
/ / Число 5 у двійковому вигляді
/ / Число 6 у двійковому вигляді
/ / Виконали операцію "і" попарно над бітами
/ / У кожній позиції
Тобто вираз 5 & 6 дорівнює 4. Виняток становить лише оператор "не" або "NOT", який для побітових операцій записується як ~ (для логічних було !). Цей оператор змінює кожен біт в числі на протилежний. Наприклад, ~ (-1) = 0. Можна легко встановити загальне правило для отримання бітового представлення негативних чисел: Якщо n - ціле позитивне число, то-n в бітовому представленні дорівнює ~ (n-1). Нарешті, залишилося розглянути лише оператори побітового зсуву. У Java є один оператор зсуву вліво і два варіанти зсуву вправо. Така відмінність пов'язана з наявністю знакового біта. При зсуві вліво оператором <<всі біти числа зміщуються на вказану кількість позицій вліво, причому звільнилися праворуч позиції заповнюються нулями. Ця операція аналогічна множенню на 2n і діє цілком передбачувано, як при позитивних, так і при негативних аргументах. Розглянемо приклади застосування операторів зсуву для значень типу int, тобто 32-бітових чисел. Нехай позитивним аргументом буде число 20, а негативним -21.
/ / Зрушення вліво для позитивного числа 20
20 <<00 = 00000000000000000000000000010100 = 20
20 <<01 = 00000000000000000000000000101000 = 40
20 <<02 = 00000000000000000000000001010000 = 80
20 <<03 = 00000000000000000000000010100000 = 160
20 <<04 = 00000000000000000000000101000000 = 320
...
20 <<25 = 00101000000000000000000000000000 = 671088640
20 <<26 = 01010000000000000000000000000000 = 1342177280
20 <<27 = 10100000000000000000000000000000 = -1610612736
20 <<28 = 01000000000000000000000000000000 = 1073741824
20 <<29 = 10000000000000000000000000000000 = -2147483648
20 <<30 = 00000000000000000000000000000000 = 0
20 <<31 = 00000000000000000000000000000000 = 0
/ / Зрушення вліво для негативного числа -21
-21 <<00 = 11111111111111111111111111101011 = -21
-21 <<01 = 11111111111111111111111111010110 = -42
-21 <<02 = 11111111111111111111111110101100 = -84
-21 <<03 = 11111111111111111111111101011000 = -168
-21 <<04 = 11111111111111111111111010110000 = -336
-21 <<05 = 11111111111111111111110101100000 = -672
...
-21 <<25 = 11010110000000000000000000000000 = -704643072
-21 <<26 = 10101100000000000000000000000000 = -1409286144
-21 <<27 = 01011000000000000000000000000000 = 1476395008
-21 <<28 = 10110000000000000000000000000000 = -1342177280
-21 <<29 = 01100000000000000000000000000000 = 1610612736
-21 <<30 = 11000000000000000000000000000000 = -1073741824
-21 <<31 = 10000000000000000000000000000000 = -2147483648
Як видно з прикладу, несподіванки виникають тоді, коли значущі біти починають займати першу позицію і впливати на знак результату. При зсуві вправо всі біти аргументу зміщуються на вказану кількість позицій, відповідно, вправо. Однак постає питання - яким значенням заповнювати позиції ліворуч, що звільняються і що відповідає за знак. Є два варіанти. Оператор>> використовує для заповнення цих позицій значення знакового біта, тобто результат завжди має той же знак, що і початкове значення. Другий оператор>>> заповнює їх нулями, тобто результат завжди позитивний.
// Зрушення вправо для позитивного числа 20
// Оператор>>
20>> 00 = 00000000000000000000000000010100 = 20
20>> 01 = 00000000000000000000000000001010 = 10
20>> 02 = 00000000000000000000000000000101 = 5
20>> 03 = 00000000000000000000000000000010 = 2
20>> 04 = 00000000000000000000000000000001 = 1
20>> 05 = 00000000000000000000000000000000 = 0
// Оператор>>>
20>>> 00 = 00000000000000000000000000010100 = 20
20>>> 01 = 00000000000000000000000000001010 = 10
20>>> 02 = 00000000000000000000000000000101 = 5
20>>> 03 = 00000000000000000000000000000010 = 2
20>>> 04 = 00000000000000000000000000000001 = 1
20>>> 05 = 00000000000000000000000000000000 = 0
Очевидно, що для позитивного аргументу оператори>> і>>> працюють абсолютно однаково. Подальший зрушення на більшу кількість позицій буде також давати нульовий результат.
// Зрушення вправо для негативного числа -21
// Оператор>>
-21>> 00 = 11111111111111111111111111101011 = -21
-21>> 01 = 11111111111111111111111111110101 = -11
-21>> 02 = 11111111111111111111111111111010 = -6
-21>> 03 = 11111111111111111111111111111101 = -3
-21>> 04 = 11111111111111111111111111111110 = -2
-21>> 05 = 11111111111111111111111111111111 = -1
// Оператор>>>
-21>>> 00 = 11111111111111111111111111101011 = -21
-21>>> 01 = 01111111111111111111111111110101 = 2147483637
-21>>> 02 = 00111111111111111111111111111010 = 1073741818
-21>>> 03 = 00011111111111111111111111111101 = 536870909
-21>>> 04 = 00001111111111111111111111111110 = 268435454
-21>>> 05 = 00000111111111111111111111111111 = 134217727
...
-21>>> 24 = 00000000000000000000000011111111 = 255
-21>>> 25 = 00000000000000000000000001111111 = 127
-21>>> 26 = 00000000000000000000000000111111 = 63
-21>>> 27 = 00000000000000000000000000011111 = 31
-21>>> 28 = 00000000000000000000000000001111 = 15
-21>>> 29 = 00000000000000000000000000000111 = 7
-21>>> 30 = 00000000000000000000000000000011 = 3
-21>>> 31 = 00000000000000000000000000000001 = 1
Як видно з прикладів, ці операції аналогічні поділу на 2n. Причому, якщо для позитивних аргументів із зростанням n результат закономірно прагне до 0, то для негативних граничним значенням є -1. ВисновокУ цій лекції були розглянуті основи лексичного аналізу програм Java. Для їх запису застосовується універсальне кодування Unicode, що дозволяє використовувати будь-яку мову крім традиційного англійського. Ще раз нагадаємо, що використання Unicode можливо і необхідно в наступних конструкціях:
Інші ж (пробіли, ключові слова, числові, булевські і null-літерали, роздільники й оператори) легко записуються із застосуванням лише ASCII-символів. У той же час будь-Unicode-символ також можна задати у вигляді спеціальної послідовності ASCII-символів. Підчасаналізукомпіляторвиділяєзтекступрограми<пробіли> (булирозглянутівсісимволи, якірозглядаютьсяякпробіли) такоментарі, якіповністювидаляютьсязкоду(булирозглянутівсівидикоментарів, зокремакоментаррозробника). Пробіли і всі види коментарів служать для розбиття тексту програми на лексеми. Були розглянуті всі види лексем, у тому числі всі види літералів. У доповненні були розглянуті особливості застосування різних операторів. Зверніть увагу на додаткові посиланняЯкщо вас цікавить...Головний розділСторінки, близькі за змістомзагрузка...
|
Теми розділу
Сторінки, близькі за змістом
|
|
Copyright © 2008—2026 Портал Знань.
При використанні матеріалів посилання, для інтернет-ресурсів — гіперпосилання, на Znannya.org обов'язкове.
Зв'язок
|
НТУУ "КПІ" Інженерія програмного забезпечення КПІ Лабораторія СЕТ |
|