Завантаження класів

Runtime-система Java звертається до класів, коли в цьому виникає необхідність Подробиці завантаження класів можуть відрізнятися для різних реалізацій Java, проте в більшості випадків використовується механізм Шляху класу для пошуку компілювати байт-коду класу, використовуваного в програмі, але не завантажувати раніше У багатьох випадках цей стандартний механізм працює відмінно, проте чимала частина достоїнств Java обумовлена ​​можливістю реалізувати завантаження класів з урахуванням специфіки додатку Щоб написати програму, в якій механізм завантаження класів відрізняється від стандартного, необхідно створити обєкт ClassLoader, який отримує байт-коди класів і завантажує їх під час виконання програми

Наприклад, ви розробляєте гру, в якій грають можуть створювати власні класи, що використовують обрану ними стратегію Для цього ви створюєте абстрактний клас Player, розширюваний гравцями для реалізації своїх ідей Коли гравці будуть готові випробувати свою стратегію, вони пересилають скомпільований байт-код класу в вашу систему Байт-код необхідно завантажити в гру, застосувати і повернути гравцеві його результат (Score) Схема виглядає наступним чином:

На сервері ігрова програма завантажує кожен очікує своєї черги клас Player, створює обєкт нового типу і дає йому можливість протиставити свою стратегію ігровому алгоритму Коли зясовується результат, він повідомляється гравцеві-розробнику стратегії

Механізм комунікацій тут не розглядається, однак він може бути спрощений до повідомлень електронної пошти, за допомогою яких гравці посилають свої класи і отримують результати

Найбільший інтерес представляє процес завантаження ігровою програмою скомпільованих класів Тут усім заправляє завантажувач класів Щоб створити завантажувач класів, слід розширити абстрактний клас Class Loader і реалізувати в підкласі метод loadClass:

protected abstract Class loadClass(String  name, boolean resolve) throws

ClassNotFoundException

Завантажує клас із заданим імям name Якщо значення resolve одно true, то метод повинен викликати resolveClass, щоб забезпечити завантаження всіх класів, використовуваних усередині заданого

У нашому прикладі ми створимо клас PlayerLoader, призначений для читання байт-коду класів зі стратегією гри та підготовки їх до роботи Основний цикл буде виглядати наступним чином:

public class Game {

static public void main (String [] args) {String name / / Імя класу

while ((name = getNextPlayer()) = null) {

try {

PlayerLoader loader = new PlayerLoader() Class

classOf = loaderloadClass(name, true)

Player

player = (Player)classOfnewInstance() Game game = new Game()

playerplay(game)

gamereportScore(name)

} catch (Exception e) {

reportException(name, e)

}

}

}

}

Для кожної нової гри потрібен свій обєкт-завантажувач PlayerLoader отже, новий клас Player не буде змішуватися з класами, завантаженими раніше Новий PlayerLoader завантажує клас і повертає представляє його обєкт Class, який використовується для створення нового обєкта класу Player Потім ми створюємо нову гру game і граємо

в неї Після її завершення повертається результат

Клас PlayerLoader розширює клас ClassLoader і задає власну реалізацію методу

loadClass:

class PlayerLoader extends ClassLoader {

private Hashtable Classes = new Hashtable()

public Class loadClass(String name, boolean resolve)

throws ClassNotFoundException

{

try {

Class newClass = (Class)Classesget(name)

if (newClass == null) {/ / ще не визначений

try {/ / перевірити, чи не є

/ / Чи системним класом

newClass = findSystemClass(name)

if (newClass = null)

return newClass

} catch (ClassNotFoundException e) {

; / / Продовжити пошук

}

/ / Клас не знайдено – його необхідно завантажити

byte[] buf = bytesForClass(name)

newClass = defineClass(buf, 0, buflength) Classesput(name, newClass)

}

if (resolve)

resolveClass (newClass)

return newClass

} catch (IOException e) {

throw new ClassNotFoundException(etoString())

}

}

/ / .. BytesForClass () і всі інші методи ..

}

Будь реалізація loadClass повинна завантажувати клас лише в тому випадку, якщо це не було зроблено раніше, тому метод містить хеш-таблицю завантажених класів Якщо клас вже присутній в ній, то повертається відповідний обєкт Class В іншому випадку loadClass спочатку перевіряє, чи можна знайти клас в локальній системі, для чого викликає метод find SystemClass класу ClassLoader цей метод здійснює пошук не лише серед системних класів (що знаходяться в пакетах java), але і в дорозі, заданому для класів Якщо при цьому буде знайдений потрібний клас, то після завантаження повертається відповідний йому обєкт Class

Якщо пошук в обох випадках закінчується безрезультатно, необхідно прочитати байт-

код класу, для чого служить метод bytesForClass:

protected byte[] bytesForClass(String name)

throws IOException, ClassNotFoundException

{

FileInputStream in = streamFor(name)

int length = inavailable () / / Отримати кількість байтів

if (length == 0)

throw new ClassNotFoundException(name)

byte[] buf = new byte[length]

inread (buf) / / Прочитати байт-код

return buf

}

В ньому використаний метод streamFor (не наводиться) для отримання потоку

FileInputStream, що містить байт-код класу Потім ми створюємо буфер потрібного розміру,

зчитуємо весь байт-код і повертаємо буфер

Коли loadClass отримує байт-код і викликається метод defineClass класу ClassLoader, в якості параметрів цей метод отримує масив byte, початкове зміщення і кількість байтів – у зазначеній частині масиву повинен знаходитися байт-код класу У даному випадку байт-код займає весь масив Коли визначення класу завершується, він додається в хеш-таблицю Classes, щоб запобігти його повторне завантаження

Після успішного завантаження класу метод loadClass повертає новий обєкт Class

Неможливо вивантажити клас, коли він перестає бути потрібним Замість цього ви просто припиняєте його використання, щоб клас-завантажувач міг бути знищений складальником сміття

Щоб отримати обєкт-завантажувач для заданого обєкта Class, слід викликати його метод getClassLoader Процес завантаження класів було розглянуто вище Якщо у даного класу відсутня клас-завантажувач, метод повертає null

Клас-завантажувач застосовується лише на першій стадії підготовки класу до роботи Всього ж існує три стадії:

1 Завантаження: отримання байт-коду з реалізацією класу

2 Компонування: пошук супертіпа класу і завантаження їх у разі потреби

3 Ініціалізація: присвоєння початкових значень статичним полів класу і виконання їх ініціалізаторів, а також всіх статичних блоків

Вправа 132

Реалізуйте класи Game і Player для якої-небудь простої гри – наприклад, хрестики-

нулики . Проведіть оцінку різних реалізацій Player за даними декількох запусків

Джерело: Арнольд К, Гослінг Д – Мова програмування Java (1997)

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*