Після написання Java-програми компілятор переводить її в байт-коди, які можна позначити як машинні команди чітко описаної моделі-т-віртуальної машини, Java, Визначення віртуальної машини
Java є ключовим в. визначенні самого <мови Java j Завдяки тому, что'прі компіляції Java-код перетворюється в байтгкоди віртуальної машини Java, а не в команди будь-якого конкретногр процесора, Java-nporpaMMa може далі виконуватися на будь-якому комп'ютері, включаючи рабочии станції UNIX, здатному до емуляції віртуальної машини Java. Слід зазначити ;, що стековая структура даних є основою визначення віртуальної машини Java.
Java-стек методів
Стеки мають важливе значення для виконуючого середовища Java-npo- грам. Поточна Java-nporpaMMa (точніше - виконується подпроцесс Java) має власний стек, званий Java-стек методів, або просто Java-стек, який використовується для спостереження за локальними змінними та іншою інформацією, необхідною для виконання методів у міру звернення до них в ході виконання програми (див. рис. 4.3).
Зокрема, під час виконання Java-програми віртуальна машина використовує стек, елементами якого є дескриптори поточних (тобто виконуються) методів. Дані дескриптори називають фреймами. Фрейм-звернення до деякого методу «cool» містить поточні значення локальних змінних і параметри методу cool, а також інформацію про метод, який викликає cool, і повернутому в цей метод значенні.
Віртуальна машина Java містить спеціальний лічильник, званий лічильником програми, який фіксує адресу команди, що виконується в даний час віртуальної машиною Java. Коли метод cool звертається до. Методу fool, поточне значення лічильника програми записується у фрейм поточного звернення до методу cool (таким чином, віртуальна машина Java «запам'ятовує» ,, кдаа їй слід повернутися після виконання методу fool). Останнім елементом Java-стека є фрейм поточного методу, тобто методу, який в даний час виконується програмою. Іншими елементами стека є фрейми призупинених методів, тобто методів, які викликали інший метод і в даний час очікують завершення його роботи, щоб продовжити своє виконання. Порядок елементів в стеку відповідає послідовності звернень до поточних методів. При зверненні до нового методу його фрейм додається в якості останнього елемента стека. Після завершення методу він видаляється з с ^ ека, а віртуальна машина Java повертається до виконання останнього припиненого методу.
Java-стек здійснює передачу параметрів методам. Слід зазначити, що Java використовує протокол передачі параметрів «виклик за значенням». Це означає, що поточним значенням змінної (ілй вираження) є те, що передається в якості аргументу викликаного методу.
- Якщо змінна х відноситься до базового типу даних, наприклад, int або float, поточне значення х є число, пов'язане з х. Якщо така змінна передається в метод, її значення присвоюється локальної змінної у фреймі викликаного методу (подібна операція присвоювання значення показана на рис. 4.3). Зверніть увагу, що при зміні викликається методом значення даної локальної змінної значення змінної в зухвалій методі не змінюється.
Мал. 4.3. Приклад Java-стека. Метод fool викликається методом cool, який раніше був викликаний методом main. Зверніть увагу на значення лічильника програми, параметрів і локальних змінних, записаних в стекових фреймах. Після завершення методу fool поновлюється звернення до методу cool, починаючи з команди 217, яка була отримана шляхом збільшення на 1 значення лічильника програми, записаного в стекового фреймі
Що стосується змінної х, яка викликає об'єкт, то її поточним значенням є адреса комірки пам'яті об'єкта х (місце зберігання адреси в пам'яті буде розглянуто в п. 4.2.3). Таким чином, коли об'єкт х передається в якості параметра в метод, по суті передається адреса комірки пам'яті. Після того як ця адреса присвоюється локальної змінної у в викликаному методі, остання звертається до того ж об'єкту, що і х. Тому, якщо викликаний метод змінює внутрішній стан цього об'єкта, то одночасно змінюється внутрішній стан об'єкта, до якого звертається х (один і той же об'єкт). Проте, якщо е ході програма У стає посиланням іншого об'єкта, х залишається без змін - змінна продовжує ставитися до первісного об'єкту.
Таким чином, віртуальна машина Java використовує Java-стек методів для виконання звернень до методів і передачі параметрів. Стеки методів не є відмінною рисою Java. Вони використовуються в виконуючих системах більшості сучасних мов програмування, в тому числі С і С ++.
рекурсія \
Одна з переваг використання стека для звернення до методу полягає в можливості використання рекурсії, тобто метод може викликати сам себе у вигляді підпрограми. Наприклад, рекурсию можна використовувати для обчислення класичного факторіального многочлена п \ = п (п - 1) (п-2) ... 1, як показано у фрагменті коду 4.7.
public static long factorial (long n) {if (n <= 1)
return 1; else
return n * factorial (n-1);
}
Фрагмент коду 4.7. Рекурсивний метод factorial '
Метод factorial рекурсивно викликає сам себе для обчислення факторіала п-1. Коли рекурсивне звернення припиняється, метод повертає (п - 1) !, яке потім множиться на п для обчислення п \. Далі рекурсивне звернення викликається для обчислення факторіала (п - 2) і так далі. Ланцюжок рекурсивних звернень і, отже, Java-ereKa триває до п, так як при зверненні до factorial 1) метод негайно повертає 1 і рекурсивного звернення не відбувається. Завдяки Java-стеку віртуальної машини метод factorial може одночасно існувати в декількох активних фреймах (в залежності від п). У кожному фреймі зберігається значення параметра п, а також повертається методом значення.
Даний приклад демонструє таку важливу властивість рекурсивного методу, як умова припинення. Дана властивість в методі factorial висловимо, записавши нерекурсівние команди в разі п <1. Рекурсивне звернення завжди виконується при меншому значенні параметра (п - 1), ніж заданий (п), таким чином на певному етапі ( «біля основи» рекурсії) виконується нерекурсівние частина програми, яка повертає 1. Проектування рекурсивного методу повинно гарантувати його припинення на певному етапі (наприклад, визначаючи рекурсивні
звернення для «малих» значень, і нерекурсівние - до «найменшим» значенням). Слід зазначити, що нескінченніше рекурсивний метод не буде виконуватися вічно. На певному етапі буде використана вся виділена для Java-стека пам'ять, що призведе до виникнення помилки браку пам'яті. Однак при правильному використанні рекурсії стек методів може реалізовувати рекурсивні методи. Використання рекурсії може бути дуже корисним, так як часто вона дозволяє створювати прості й ефективні програми для вирішення досить складних задач.
стек операндів
Існує ще один спосіб використання стеків віртуальною машиною Java. Стек операндів використовується для обчислення арифметичних виразів, наприклад, ((а + b) * (c + d)) / e. Проста операція з двома операндами, наприклад, а + Ь, обчислюється таким чином: а додається в стек операндів, потім b додається в цей стек, після чого викликається команда, яка витягує ці два значення з стека операндов, виконує бінарну операцію і додає результат теж в стек операндів. Подібним чином для додавання і вилучення елементів з пам'яті в стеку операндів використовуються методи pop і push.
У розділі 6 розглянемо обчислення арифметичних виразів за допомогою більш загальної схеми. На даному етапі просто підкреслимо, що стек операндів відіграє значну роль при обчисленні арифметичних операцій. Таким чином, стек операндів і Java-стек методів є базовими компонентами віртуальної машини Java.
Джерело: Гудріч М.Т. Г93 Структури даних і алгоритми в Java / М.Т. Гудріч, Р. Тамассі; Пер. з англ. AM Чорнух. - Мн .: Нове знання, 2003. - 671 е .: мул.