Текст книги "Java: руководство для начинающих (ЛП)"
Автор книги: Герберт Шилдт
Жанр:
Программирование
сообщить о нарушении
Текущая страница: 25 (всего у книги 36 страниц)
При остановке имитатора работы часов вызывается метод not if у (). Это нужно для того, чтобы возобновить исполнение ждущего потока. Как упоминалось выше, в обоих методах, tick() и tock (), после вывода сообщения на экран вызывается метод wait (). В результате при остановке имитатора работы часов один из потоков обязательно будет находиться в состоянии ожидания. Следовательно, последний вызов метода notify () необходим. В качестве эксперимента попробуйте удалить вызов метода notify () и посмотрите, что при этом произойдет. Вы увидите, что программа зависнет, и вам придется завершить ее нажатием комбинации клавиш . Дело в том, что когда метод tock () в последний раз получает управление, он вызывает метод wait (), после чего не происходит вызов метода not if у (), позволяющего завершиться методу tock (). В итоге метод tock () остается в состоянии бесконечного ожидания.
Если у вас еще остаются сомнения по поводу того, что методы wait () и notify () необходимы для организации нормального выполнения программы, имитирующей работу часов, замените в ее исходном коде класс TickTock приведенным ниже его вариантом. Он отличается тем, что в нем удалены вызовы методов wait () и notify (). // В этой версии вызовы методов wait() и notify() отсутствуют, class TickTock { String state; // содержит сведения о состоянии часов synchronized void tick(boolean running) { if(!running) { // остановить часы state = "ticked"; return; } System.out.print("Tick "); state = "ticked"; // установить текущее состояние после такта "тик" } synchronized void tock(boolean running) { if(!running) { // остановить часы state = "tocked"; return; } System.out.println("Tock") ; state = "tocked"; // установить текущее состояние после такта "так" } }
Теперь программа выводит на экран следующие сообщения: Tick Tick Tick Tick Tick Tock Tock Tock Tock Tock
Это происходит потому, что методы tick() и tock() не взаимодействуют друг с другом. Приостановка, возобновление и остановка потоков
Иногда оказывается полезно приостановить или даже полностью прекратить исполнение потока. Допустим, отдельный поток используется для отображения времени. Если пользователю не нужны часы на экране, то отображающий их поток можно приостановить. Независимо от причин, по которым требуется временная остановка потока, сделать это нетрудно, как, впрочем, и возобновить исполнение потока.
Механизмы приостановки, возобновление и остановки потоков менялись в разных версиях Java. До появления версии Java 2 для этих целей применялись методы suspend (), resume () и stop (), определенные в классе Thread. Ниже приведены общие формы их объявления. final void resume() final void suspend() final void stop()
На первый взгляд кажется, что упомянутые выше методы удобны для управления потоками, но пользоваться ими все же не рекомендуется по следующим причинам. При выполнении метода suspend () иногда возникают серьезные осложнения, приводящие к взаимоблокировке. Метод resume () сам по себе безопасен, но применяется только в сочетании с методом suspend (). Что же касается метода stop () из класса Thread, то и он не рекомендуется к применению, начиная с версии Java 2, поскольку может вызывать порой серьезные осложнения в работе многопоточных программ.
Если методы suspend (), resume () и stop () нельзя использовать для управления потоками, то может показаться, что приостановить, возобновить и остановить поток вообще нельзя. Но это, к счастью, не так. Поток следует разрабатывать таким образом, чтобы в методе run () периодически осуществлялась проверка, следует ли приостановить, возобновить или остановить поток. Обычно для этой цели используются две флаговые переменные: одна – для приостановки и возобновления потока, другая – для остановки потока. Если флаговая переменная, управляющая приостановкой потока, установлена в состояние исполнения, то метод run () должен обеспечить продолжение исполнения потока. Если же эта флаговая переменная находится в состоянии приостановки, в работе потока должна произойти пауза. А если переменная, управляющая остановкой потока, находится в состоянии остановки, исполнение потока должно прекратиться.
Следующий пример программы демонстрирует один из способов реализации собственных версий методов suspend (), resume () и stop (). // Приостановка, возобновление и остановка потока. class MyThread implements Runnable { Thread thrd; // Если эта переменная принимает логическое значение // true, исполнение потока приостанавливается. volatile boolean suspended; // Если эта переменная принимает логическое значение // true, исполнение потока прекращается. volatile boolean stopped; MyThread(String name) { thrd = new Thread(this, name); suspended = false; stopped = false; thrd.start(); } // Точка входа в поток public void run() { System.out.println(thrd.getName() + " starting."); try { for(int i = 1; i < 1000; i++) { System.out.print(i + " "); if((i %10)==0) { System.out.println() ; Thread.sleep(250) ; } // Для проверки условий приостановки и остановки потока // используется следужхций синхронизированный блок. synchronized(this) { while(suspended) { wait(); } if(stopped) break; } } } catch (InterruptedException exc) { System.out.println(thrd.getName() + " interrupted."); } System.out.println(thrd.getName() + " exiting."); } // остановить поток synchronized void mystopO { stopped = true; // Следующие операторы обеспечивают полную // остановку приостановленного потока, suspended = false; notify(); } // приостановить поток synchronized void mysuspend() { suspended = true; } // возобновить поток synchronized void myresume() { suspended = false; notify(); } } class Suspend { public static void main(String args[]) { MyThread obi = new MyThread("My Thread"); try { Thread.sleep(1000); // позволить потоку оЫ начать исполнение obi.mysuspend(); System.out.println("Suspending thread."); Thread.sleep(1000); obi.myresume(); System.out.println("Resuming thread."); Thread.sleep(1000); obi.mysuspend(); System.out.println("Suspending thread."); Thread.sleep(1000); obi.myresume(); System.out.println("Resuming thread.") ; Thread.sleep(1000); obi.mysuspend() ; System.out.println("Stopping thread."); obi.mystop(); } catch (InterruptedException e) { System.out.println("Main thread Interrupted"); } // ожидать завершения потока try { obi.thrd.join() ; } catch (InterruptedException e) { System.out.println("Main thread Interrupted"); } System.out.println("Main thread exiting."); } }
Ниже приведен результат выполнения данной программы. My Thread starting. 123456789 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 Suspending thread. Resuming thread. 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 Suspending thread. Resuming thread. 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 Stopping thread. My Thread exiting. Main thread exiting.
Эта программа работает следующим образом. В классе потока MyThread определены две логические переменные, suspended и stopped, управляющие временной и полной остановкой потока. В конструкторе этого класса обеим переменным присваивается логическое значение false. Метод run () содержит синхронизированный блок, в котором проверяется состояние переменной suspended. Если эта переменная принимаетлогическое значение true, вызывается метод wait (), приостанавливающий исполнение потока. Логическое значение true присваивается переменной suspended в методе mysuspend (), и поэтому данный метод следует вызвать для приостановки потока. Для возобновления потока служит метод myresume (), в котором переменной suspended присваивается логическое значение false и вызывается метод not if у ().
Для остановки потока следует вызвать метод my stop (), в котором переменной stopped присваивается логическое значение true. Кроме того, в методе mystop () переменной suspended присваивается логическое значение false и вызывается метод notify (). Это необходимо для прекращения работы потока, исполнение которого ранее было приостановлено.
В отношении рассматриваемой здесь программы нужно сделать еще одно, последнее замечание. В объявлении переменных suspended и stopped используется ключевое слово volatile. Этот модификатор подробно описывается в главе 14, а до тех пор вкратце поясним его назначение. Он сообщает компилятору о том, что значение переменной может быть неожиданно изменено другими частями программы, в том числе и другим потоком.
Пример для опробования 11.2. Применение основного потока
В каждой программе на Java присутствует хотя бы один поток, называемый основным. Этот поток получает управление автоматически при запуске программы на выполнение. В этом проекте будет продемонстрировано, что основным потоком можно управлять таким образом же, как и любым другим.
Последовательность действий
Создайте файл UseMain.java.
Для доступа к основному потоку нужно получить ссылающийся на него объект типа Thread. Для этого следует вызвать метод currentThread (), являющийся статическим членом класса Thread. Ниже приведено объявление этого метода. static Thread currentThread() Метод currentThread () возвращает ссылку на тот поток, из которого он вызывается. Так, если вызвать метод currentThread () из основного потока, можно получить ссылку на этот поток. А имея ссылку на основной поток, можно управлять им.
Введите в файл UseMain. j ava приведенный ниже исходный код программы. В процессе ее выполнения сначала извлекается ссылка на основной поток, затем определяется и устанавливается имя и приоритет потока. /* Пример для опробования 11.2. Управление основным потоком. */ class UseMain { public static void main(String args[]) { Thread thrd; // получить основной поток thrd = Thread.currentThread(); // отобразить имя основного потока System.out.println("Main thread is called: " + thrd.getName()); // отобразить приоритет основного потока System.out.println("Priority: " + thrd.getPriority()); System.out.println(); // установить имя и приоритет основного потока System.out.println("Setting name and priority.n"); thrd.setName("Thread #1"); thrd.setPriority(Thread.NORM_PRI0RITY+3); System.out.println("Main thread is now called: " + thrd.getName()); System.out.println("Priority is now: " + thrd.getPriority()); } }
Ниже приведен результат выполнения данной программы. Main thread is called: main Priority: 5 Setting name and priority. Main thread is now called: Thread #1 Priority is now: 8
Выполняя операции над основным потоком, необходимо соблюдать осторожность. Так, если добавить в конце метода main () приведенный ниже код, программа никогда не завершится, потому что будет ожидать завершения основного потока!try { thrd.join(); } catch(InterruptedException exc) { System.out.println("Interrupted"); } Упражнение для самопроверки по материалу главы 11
Каким образом имеющиеся в Java средства многопоточного программирования позволяют писать более эффективные программы?
Для поддержки многопоточного программирования в Java предусмотрен класс и интерфейс .
В каких случаях следует отдать предпочтение расширению класса Thread над реализацией интерфейса Runnable?
Покажите, как с помощью метода j oin () можно организовать ожидание завершения потокового объекта MyThrd.
Покажите, как установить приоритет потока MyThrd на три уровня выше нормального приоритета.
Что произойдет, если в объявлении метода указать ключевое слово synchronized?
Методы wait () и notify () служат для __ .
Внесите в класс TickTock изменения для организации настоящего отчета времени. Первую половину секунды должен занимать вывод на экран слова "Tick", а вторую – вывод слова "Tock". Таким образом, сообщение "Tick-Tock" должно соответствовать одной секунде отсчитываемого времени. (Время переключения контекстов можно не учитывать.)
Почему в новых программах на Java не следует применять методы suspend(), resume() и stop()?
С помощью какого метода из класса Thread можно получить имя потока?
Какое значение возвращает метод isAlive () ?
Попытайтесь самостоятельно реализовать средства синхронизации в классе Queue, разработанном в предыдущих главах. В результате доработки класс должен действовать правильно, когда он используется для многопоточной обработки.
Глава 12 Перечисления, автоупаковка, статический импорт и аннотации
Основные навыки и понятия
Представление о перечислениях
Применение свойств перечислений, основанных на классах
Применение методов values () и valueof () к перечислениям
Создание перечислений с конструкторами, переменными экземпляра и методами
Применение методов ordinal () и compareTo (), наследуемых перечислениями от класса Enum
Использование оболочек типов Java
Основные положения об автоупаковке и автораспаковке
Применение автоупаковки в методах
Употребление автоупаковки в выражениях
Применение статического импорта
Основные положения об аннотациях
В этой главе рассматриваются четыре относительно новые языковые средства Java: перечислинения, автоупаковка, статический импорт и аннотации. И хотя ни одно из них не вошло в первоначальное описание Java, каждое из них расширяет возможности и область применения этого языка программирования. Так, перечисления и автоупаковка удовлетворяют давно назревшим потребностям, статический импорт упрощает применение статических членов класса, тогда как аннотации расширяют виды информации, которую можно встраивать в исходный файл. А сообща все эти средства обеспечивают более совершенное решение насущных задач программирования. В этой главе рассматриваются также оболочки типов Java. Перечисления
Несмотря на то что перечисления применяются во многих языках программирования, в первоначальном описании Java они не поддерживалась. Это, в частности, объясняется тем, что перечисление является скорее удобным, чем обязательным языковым средством. Но со временем программирующие на Java все чаще стали ощущать потребность в перечислениях, потому что с их помощью они могли реализовать структурно изящные решения многих задач программирования. И эта потребность была учтена в версии JDK 5, начиная с которой перечисления внедрены в Java.
В простейшем виде перечисление представляет собой список именованных констант, определяющих новый тип данных. Объект перечислимого типа может принимать лишь значения, содержащиеся в списке. Таким образом, перечисления предоставляют возможность точно определить новый тип данных, имеющий строго фиксированный ряд допустимых значений.
В повседневной жизни перечисления встречаются довольно часто. Например, к ним можно отнести ряд монет, имеющих хождение в стране. А месяцы года и дни недели перечисляются по названиям.
С точки зрения программирования перечисления оказываются удобными в тех случаях, когда требуется определить ряд значений, обозначающих совокупность элементов. Например, с помощью перечисления можно представить набор кодов состояния (успешное завершение, ошибка, необходимость повторной попытки). Конечно, такие значения можно определить и с помощью констант типа final, но перечисления обеспечивают более структурированный подход к решению подобной задачи. Основные положения о перечислениях
Для создания перечисления служит ключевое слово enum. Ниже приведен пример простого перечисления разных видов транспортных средств. // Перечисление видов транспортных средств, enum Transport { CAR, TRUCK, AIRPLANE, TRAIN, BOAT }
Идентификаторы CAR, TRUCK и так далее называются константами перечислимого типа. Каждый из них автоматически неявно объявляется как открытый (public), статический (static) член перечисления Transport. Тип этих констант соответствует типу перечисления (в данном случае – Transport). В терминологии Java подобные константы называются самотипизированными (приставка “само” означает, что в качестве типа константы принимается тип перечисления).
Определив перечисление, можно создать переменную данного типа. Но, несмотря на то, что перечисление определяется как тип класса, получить экземпляр объекта типа enum с помощью оператора new нельзя. Переменная перечислимого типа создается подобно переменной простого типа. Например, для объявления переменной tp упомянутого выше перечислимого типа Transport служит следующее выражение: Transport tp;
Переменная tp относится к типу Transport, и поэтому ей можно присваивать только те значения, которые определены в данном перечислении. Например, в следующей строке кода переменной tp присваивается значение AIRPLANE: tp = Transport.AIRPLANE;
Обратите внимание на то, что идентификатор AIRPLANE полностью определяется как относящийся к типу Transport.
Для проверки равенства констант перечислимого типа служит оператор сравнения =. Например, в приведенной ниже строке кода содержимое переменной tp сравнивается с константой TRAIN, if(tp == Transport.TRAIN) // ...
Перечислимые значения можно также использовать в операторе switch. Очевидно, что в ветвях case этого оператора могут присутствовать только константы того же самого перечислимого типа, что и в выражении switch. Например, следующий фрагмент кода составлен правильно: // Применение перечисления для управления оператором switch, switch(tp) { case CAR: // ... case TRUCK: // ...
Как видите, в ветвях case используются константы без полного определения имени типа. Например, вместо Transport. TRUCK указано просто TRUCK. Это допустимо потому, что перечислимый тип в выражении switch неявно определяет тип констант в ветвях case. Более того, если попытаться указать тип константы явно, при компиляции возникнет ошибка.
При отображении константы перечислимого типа, например, с помощью метода println (), выводится ее имя. Так, в результате выполнения следующего оператора отобразится имя BOAT: System.out.println(Transport.BOAT);
Ниже приведен пример программы, демонстрирующий все особенности применения перечисления Transport. // Особенности применения перечисления Transport. // Объявление перечисления. enum Transport { CAR, TRUCK, AIRPLANE, TRAIN, BOAT } class EnumDemo { public static void main(String args[]) { // Объявление ссылки на перечисление Transport. Transport tp; // Присваивание переменной tp константы AIRPLANE. tp = Transport.AIRPLANE; // вывести перечислимое значение System.out.println("Value of tp: " + tp) ; System.out.println(); tp = Transport.TRAIN; // Проверка равенства двух объектов типа Transport. if(tp == Transport.TRAIN) // сравнить два перечислимых значения System.out.println("tp contains TRAIN.n"); // Использование перечисления для управления оператором switch. switch(tp) { case CAR: System.out.println("A car carries people."); break; case TRUCK: System.out.println("A truck carries freight."); break; case AIRPLANE: System.out.println("An airplane flies."); break; case TRAIN: System.out.println("A train runs on rails."); break; case BOAT: System.put.println("A boat sails on water."); break; } } }
Результат выполнения данной программы выглядит следующим образом: Value of tp: AIRPLANE tp contains TRAIN. A train runs on rails.
Прежде чем переходить к рассмотрению следующей темы, необходимо сделать одно замечание. Имена констант в перечислении Transport указываются прописными буквами (например, одна из них называется CAR, а не саг). Но это требование не является обязательным. Не существует никаких ограничений на использование регистра символов в именах констант перечислимого типа. Но поскольку константы перечислимого типа, как правило, предназначены для замены переменных, объявленных как final, имена которых по традиции обозначаются прописными буквами, то и имена констант перечислимого типа, как правило, обозначаются прописными буквами. И хотя на этот счет имеются разные точки зрения, в примерах программ, представленных в этой книге, для констант перечислимого типа будут выбираться имена, обозначаемые прописными буквами. Перечисления в Java относятся к типам классов
Приведенные выше примеры демонстрируют создание и использование перечислений, но они не дают полного представления обо всех их возможностях. В отличие от других языков программирования перечисления в Java реализованы как типы классов. И хотя создать экземпляр объекта типа enum с помощью оператора new нельзя, во всем остальном перечисления ничем не отличаются от классов. Такой способ реализации перечислений наделяет их богатыми возможностями, принципиально недостижимыми в других языках. Это, в частности, позволяет определять конструкторы перечислений, добавлять в них переменные экземпляра, методы и даже создавать перечисления, реализующие интерфейсы. Методы values () и vaJueOf ()
У всех перечислений имеются два предопределенных метода: values () и valueOf ().
Ниже приведены общие формы их объявления. public static перечислимый_тип[] values() public static перечислимый_тип valueOf(String str)
Метод values () возвращает массив, содержащий константы перечислимого типа, а метод valueOf () – константу того же типа, значение которой соответствует символьной строке str, передаваемой этому методу в качестве параметра. В обоих чаях перечислимый_тип обозначает тип используемого перечисления. Так, если воспользоваться рассмотренным выше перечислением Transport, то при вызове метода Transport .valueOf ("TRAIN") будет возвращено значение TRAIN типа Transport. Ниже приведен пример программы, демонстрирующий применение методов values () и valueOf(). // Применение встроенных в перечисление методов. // Перечисление разных видов транспорта, enum Transport { CAR, TRUCK, AIRPLANE, TRAIN, BOAT } class EnumDemo2 { public static void main(String args[]) { Transport tp; System.out.println("Here are all Transport constants"); // воспользоваться методом values() // Получение массива констант типа Transport. Transport allTransports[] = Transport.values(); for(Transport t : allTransports) System.out.println(t); System.out.println(); // воспользоваться методом valueOf() // Получение константы под названием AIRPLANE. tp = Transport.valueOf("AIRPLANE"); System.out.println("tp contains " + tp); } }
Выполнение этой программы дает следующий результат: Here are all Transport constants CAR TRUCK AIRPLANE TRAIN BOAT tp contains AIRPLANE
Обратите внимание на то, что в данном примере программы для перебора массива констант, полученного с помощью метода values (), используется вариант for-each цикла for. Ради большей наглядности данного примера в его коде создается переменная allTransports, которой присваивается ссылка на массив констант перечислимого типа. Но делать это совсем не обязательно, а цикл for можно написать так, как показано ниже. В этом случае необходимость в дополнительной переменной allTransports отпадает. for(Transport t : Transport.values ()) System.out.println(t);
Обратите также внимание на то, что значение, соответствующее имени AIRPLANE, было получено в результате вызова метода valueOf (): tp = Transport.valueOf("AIRPLANE");
Как пояснялось выше, метод valueOf () возвращает значение перечислимого типа, связанное с именем константы, которое передается в виде символьной строки при вызове этого метода. Конструкторы, методы, переменные экземпляра и перечисления
Следует иметь в виду, что каждая константа перечислимого типа является объектом этого же типа, а следовательно, в перечислении можно определить конструкторы, ввести методы и объявить переменные экземпляра. Если определить для объекта перечислимого типа enum конструктор, он будет вызываться при создании каждой константы этого типа. А каждая константа перечислимого типа позволяет вызвать любой метод, определенный в перечислении. И у каждой константы перечислимого типа имеется собственная копия любой переменной экземпляра, определенной в перечислении. Ниже приведен пример с новой версией перечисления Transport, демонстрирующий применение конструктора, переменной экземпляра и метода. Благодаря им появляется возможность определить обычную скорость передвижения различных транспортных средств. // Применение конструктора, переменной экземпляра и // метода в перечислении, enum Transport { // Обратите внимание на инициализирующие значения констант. CAR(65), TRUCK(55), AIRPLANE(600), TRAIN(70), BOAT(22); // Объявление переменной экземпляра. private int speed; // обычная скорость каждого транспортного средства // Объявление конструктора. Transport(int s) { speed = s; } // Определение метода. int getSpeed() { return speed; } } class EnumDemo3 { public static void main(String args[]) { Transport tp; // отобразить скорость самолета // Скорость определяется с помощью метода getSpeed(). System.out.println("Typical speed for an airplane is " + Transport.AIRPLANE.getSpeed() + " miles per hour.n"); // отобразить все виды транспорта и скорости их передвижения System.out.println("All Transport speeds: "); for(Transport t : Transport.values()) System.out.println(t + " typical speed is " + t.getSpeedO + " miles per hour."); } }
Выполнение этой программы дает следующий результат: Typical speed for an airplane is 600 miles per hour. All Transport speeds: CAR typical speed is 65 miles per hour. TRUCK typical speed is 55 miles per hour. AIRPLANE typical speed is 600 miles per hour. TRAIN typical speed is 70 miles per hour. BOAT typical speed is 22 miles per hour.
В эту версию перечисления Transport внесен ряд дополнений. Во-первых, появилась переменная экземпляра speed, используемая для хранения скорости передвижения каждого вида транспортных средств. Во-вторых, в перечисление Transport добавлен конструктор, которому передается значение скорости. И в-третьих, в это перечисление добавлен метод getSpeedO , возвращающий значение переменной speed, т.е. скорость передвижения конкретного транспортного средства.
Когда переменная tp объявляется в методе main (), конструктор Transport () автоматически вызывается для каждой указанной константы. Аргументы, передаваемые конструктору, указываются в скобках после имени константы, как показано ниже. CAR(65), TRUCK(55), AIRPLANE(600), TRAIN(70), BOAT(22);
Числовые значения, передаваемые конструктору Transport () в качестве параметра s, присваиваются переменной speed. Обратите также внимание на то, что список констант перечислимого типа завершается точкой с запятой. Последней в этом списке указана константа BOAT. Точка с запятой требуется в том случае, если, помимо констант, в перечислении присутствуют и другие члены.
У каждой константы перечислимого типа имеется собственная копия переменной speed, что дает возможность выяснить скорость передвижения конкретного транспортного средства, вызвав метод getSpeed (). Например, в методе main () скорость самолета определяется при следующем вызове: Transport.AIRPLANE.getSpeed()
Скорость каждого транспортного средства определяется путем перебора констант перечислимого типа в цикле for. А поскольку у каждой такой константы имеется своя копия переменной speed, то значения скорости, связанные с разными константами, отличаются друг от друга. Это довольно эффективный принцип организации перечислений, но он возможен только в том случае, если перечисления реализованы в виде классов, как это сделано в Java.
В предыдущем примере использовался только один конструктор, но перечисления, как и обычные классы, допускают любое число конструкторов. Два важных ограничения
На перечисления накладываются два ограничения. Во-первых, перечисление не может быть подклассом другого класса. И во-вторых, перечисление не может выступать в роли суперкласса. Иными словами, перечислимый тип enum нельзя расширять. Если бы это было не так, перечисления действовали бы как обычные классы. Основной же особенностью перечислений является создание констант в виде объектов того класса, в котором они были объявлены. Наследование перечислений от класса Enum
Несмотря на то что при объявлении перечислимого типа enum нельзя указывать суперкласс, все перечисления автоматически наследуют переменные и методы от класса java.lang.Enum. В этом классе определен ряд методов, доступных для использования всеми перечислениями. И хотя большинство этих методов используются редко, тем не менее два из них иногда применяются в программах на Java. Это методы ordinal () и compareTo().
Метод ordinal () принимает значение, обозначающее положение константы перечислимого типа в списке. Это значение принято называть порядковым. Ниже приведена общая форма объявления метода ordinal (). final int ordinal()
Этот метод возвращает порядковое значение вызывающей константы. Отсчет порядковых значений начинается с нуля. Следовательно, в упоминавшемся выше перечислении Transport порядковое значение константы CAR равно нулю, константы TRUCK – 1, константы AIRPLANE – 2 И Т.Д.
Для сравнения порядковых значений двух констант в одном и том же перечислении можно воспользоваться методом compareTo (). Ниже приведена общая форма объявления этого метода. final int compareTo(перечислимый_тип е)
Здесь в качестве параметра е задается константа, сравниваемая с вызывающей константой, а перед ней указывается перечислимый_тип, к которому эта константа относится. Следует иметь в виду, что вызывающая константа и константа е должны относиться к одному и тому же перечислимому типу. Так, если порядковое значение вызывающей константы оказывается меньше, чем у константы е, метод compareTo () возвращает отрицательное значение. Если же их порядковые значения совпадают, вращается нулевое значение. И наконец, если порядковое значение вызывающей константы больше, чем у константы е, метод возвращает положительное значение.
Ниже приведен пример программы, демонстрирующий применение методов ordinal() иcompareTo(). // Применение методов ordinal() и compareTo(). // Перечисление разных видов транспорта, enum Transport { CAR, TRUCK, AIRPLANE, TRAIN, BOAT } class EnumDemo4 { public static void main(String args[]) { Transport tp, tp2, tp3; // получить все порядковые значения с помощью метода ordinal() System.out.println("Here are all Transport constants" + " and their ordinal values: "); for(Transport t : Transport.values()) // Получение порядковых значений констант. System.out.println(t + " " + t.ordinal()); tp = Transport.AIRPLANE; tp2 = Transport.TRAIN; tp3 = Transport.AIRPLANE; System.out.println(); // продемонстрировать применение метода сошрагеТо() // Сравнение порядковых значений констант. if(tp.compareTo(tp2) < 0) System.out.println(tp + " comes before " + tp2) ; if(tp.compareTo(tp2) > 0) System.out.println(tp2 + " comes before " + tp); if(tp.compareTo(tp3) == 0) System.out.println(tp + " equals " + tp3); } }