Программирование на Java

Базовые типы данных

Назад Вперед
В языке Java определено восемь базовых типов данных. Для каждого базового типа данных отводится конкретный размер памяти. Этот размер, как мы говорили в предыдущем разделе, не зависит от платформы, на которой выполняется приложение Java:

Тип данных Размер занимаемой области памяти Значение по умолчанию
boolean 8 false
byte 8 0
char 16 'x0'
short 16 0
int 32 0
long 64 0
float 32 0.0F
double 64 0.0D

Фактически размеры памяти, отведенные для хранения переменной, могут отличаться от приведенных выше, например, для хранения переменной типа short может быть зарезервировано слово размером 32 бита. Однако язык Java сделан таким образом, что это никак не повлияет на мобильность приложения. Так как в языке Java нет указателей, вы не можете адресоваться к элементам массива чисел по относительному смещению этих элементов в оперативной памяти. Следовательно, точный размер элемента в данном случае не играет никакой роли.
Все базовые типы данных по умолчанию инициализируются, поэтому программисту не нужно об этом беспокоиться. Вы можете также инициализировать переменные базовых типов в программе или при их определении, как это показано ниже:
int nCounter = 0; int i; i = 8;
Переменные типа boolean могут находиться только в двух состояниях - true и false, причем эти состояния никаким образом нельзя соотнести с целыми значениями. Вы не можете, как это было в языке С, выполнить преобразование типа boolean, например, к типу int - компилятор выдаст сообщение об ошибке.
Переменная типа byte занимает восемь бит памяти и про нее больше нечего сказать.
Что же касается типа char, то он используется для хранения символов в кодировке UNICODE. Эта кодировка позволяет хранить национальные наборы символов, что очень удобно для интернациональных приложений, предназначенных для работы в Internet.
Переменные типа byte, short, int и long являются знаковыми. В языке Java нет беззнаковых переменных, как это было в языке С.
Приложение Java может оперировать числами в формате с плавающей точкой, определенным в спецификации IEEE 754. Тип float позволяет хранить числа с одинарной точностью, а формат double - с двойной.

Библиотека классов java.applet

Как нетрудно догадаться из названия, библиотека классов java.applet инкапсулирует поведение аплетов Java. Когда вы будете создавать свои аплеты, вам будет нужен класс Applet, расположенный в этой библиотеке классов. Дополнительно в библиотеке классов java.applet определены интерфейсы для подключения аплетов к содержащим их документам и классы для проигрывания звуковых фрагментов.
Назад Вперед

Библиотека классов java.appletБиблиотека классов java.applet Контакты
О компании
Новости
Вакансии
Правовые аспекты
Условия использования
Торговые марки

Copyright 1994-2005 Sun Microsystems, Inc.
Библиотека классов java.applet
printmenus();
Библиотека классов java.appletПрограммные продукты
Рабочие станции и тонкие клиенты
Серверы
Системы хранения данных
Посмотреть все Библиотека классов java.applet»


Библиотека классов java.appletSolaris 10
Java 2 Standard Edition
Developer Tools
Top Downloads
New Downloads
Патчи и обновления
Посмотреть все Библиотека классов java.applet»


Библиотека классов java.appletКаталог решений
Истории успеха
The Sun Grid
Партнерские программы
Посмотреть все Библиотека классов java.applet»


Библиотека классов java.appletГарантийное обслуживание
Программы SunSpectrum
Консалтинг
Услуги инсталляции
Поддержка ПО
Посмотреть все Библиотека классов java.applet»


Библиотека классов java.appletОписание курсов
Сертификация
Авторизованные учебные центры
Посмотреть все Библиотека классов java.applet»


Библиотека классов java.appletПроекты
События
Lab Downloads
Посмотреть все Библиотека классов java.applet»


Библиотека классов java.applet

Библиотека классов java.awt.image

В среде любой операционной системы работа с графическими изображениями является достаточно сложной задачей. В операционной системе Windows для этого применяется графический интерфейс GDI. Если вы будете рисовать графические изображения в среде OS/2 или X-Windows, вам, очевидно, придется использовать другой программный интерфейс. Большую сложность также вызывает разбор заголовков графических файлов, так как они могут иметь различный формат и иногда содержат неправильную или противоречивую информацию.
Когда вы программируете на Java, рисование и обработка графических изображений выполняется намного проще, так как вам доступна специально предназначенная для этого библиотека классов java.awt.image. Помимо широкого разнообразия и удобства определенных в ней классов и методов, отметим способность этой библиотеки работать с графическими изображениями в формате GIF. Этот формат широко используется в Internet, так как он позволяет сжимать файлы графических изображений во много раз без потери качества за счет устранения избыточности.

Библиотека классов java.awt.peer

Библиотека классов java.awt.peer служит для подключения компонент AWT (например, кнопок, списков, полей редактирования текстовой информации, переключателей и так далее) к реализациям, зависящим от платформы, в процессе создания этих компонент.

Библиотека классов java.awt

Для создания пользовательского интерфейса аплеты Java могут и должны использовать библиотеку классов java.awt. AWT - это сокращение от Abstract Window Toolkit (инструментарий для работы с абстрактными окнами).
Классы, входящие в состав библиотеки java.awt, предоставляют возможность создания пользовательского интерфейса способом, не зависящим от платформы, на которой выполняется аплет Java. Вы можете создавать обычные окна и диалоговые панели, кнопки, переключатели, списки, меню, полосы просмотра, однострочные и многострочные поля для ввода текстовой информации.

Библиотека классов java.io

В библиотеке классов java.io собраны классы, имеющие отношение к вводу и выводу данных через потоки. Заметим, что с использованием этих классов можно работать не только с потоками байт, но также и с потоками данных других типов, например числами int или текстовыми строками.

Библиотека классов java.net

Язык программирования Java разрабатывался в предположении, что им будут пользоваться для создания сетевых приложений. Поэтому было бы странно, если бы в составе среды разработки приложений Java не поставлялась библиотека классов для работы в сети. Библиотека классов java.net предназначена как раз для этого. Она содержит классы, с помощью которых можно работать с универсальными сетевыми адресами URL, передавать данные с использованием сокетов TCP и UDP, выполнять различные операции с адресами IP. Эта библиотека содержит также классы для выполнения преобразований двоичных данных в текстовый формат, что часто бывает необходимо.
В качестве примера приложения, составленного на языке программирования Java и ориентированного на работу в сети Internet, можно привести игру Java Color Lines (рис. 7).

Библиотека классов java.net Рис. 7. Сетевая игра Java Color Lines.
Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

Это сетевая версия известной игры Lines, которая выполнена в виде нескольких аплетов, взаимодействующих между собой и между сервером Web, на котором они расположены. Так как список имен игроков и достигнутых ими результатов хранится на сервере, вы можете поучаствовать в мировом турнире, сразившись с игроками из разных стран.

Библиотека классов java.util

Библиотека классов java.util очень полезна при составлении приложений, потому что в ней имеются классы для создания таких структур, как динамические массивы, стеки и словари. Есть классы для работы с генератором псевдослучайных чисел, для разбора строк на составляющие элементы (токены), для работы с календарной датой и временем.

Библиотеки классов Java

Назад Вперед
Если предоставить в распоряжение программиста только язык программирования и не снабдить его набором готовых модулей, предназначенных для решения самых распространенных задач, ему придется отвлекаться на множество мелких деталей.
Обычно все профессиональные системы разработки приложений на языке программирования C++ содержат в своем составе набор стандартных библиотечных функций или библиотеки классов. В комплекте со всеми средствами разработки Java поставляются достаточно развитые библиотеки классов, значительно упрощающие программирование. В этом разделе мы кратко расскажем о составе и назначении библиотек классов Java.

Браузеры

Конечно, основная линия развития оставалась связанной с браузерами. Хотя Internet только начинал наполняться все новыми технологиями, уже возникали проблемы совместимости. Под разными платформами работали настолько разные браузеры, что различались даже шрифты. В результате автор мог создать красивую аккуратную страницу, которая у клиента расползалась.
С помощью Java web-страницу можно наполнить не только обычным текстом, но и динамическими элементами - простыми видеовставками типа вращающегося земного шара или Дьюка, машущего рукой (хотя сейчас такие задачи хорошо решает анимированный GIF, а в более сложных случаях - Macromedia Flash); интерактивными элементами типа вращающейся модели химической молекулы; бегущими строками, содержащими, например, биржевые индексы или прогноз погоды.
Но на самом деле Java - это больше, чем украшение HTML. Поскольку это полноценный язык программирования, с его помощью можно создать сложный пользовательский интерфейс. В самой первой версии Java Development Kit (средство разработки на Java) был пример апплета, представляющий простейшие электронные таблицы. Вскоре появился текстовый редактор, позволяющий менять стиль и цвет текста. Конечно, были игровые апплеты, обучающие, моделирующие физические и иные системы. Например, клиент, сделавший заказ в магазине или отправивший посылку почтой, получал возможность следить за доставкой через Internet.
В отличие от обычных программ, апплеты получили "в наследство" важное свойство HTML-страниц. Прочитав сегодня содержание страницы новостей, клиент не сохраняет ее на своем компьютере, а на следующий день читает обновленное содержание. Точно так же, скачав апплет и поработав с ним, можно удалить его, а в следующий раз получить более новую версию. Таким образом, программы появляются и исчезают с машины клиента безо всякого усилия, не требуются ни специальные знания, ни действия, и при этом автоматически поддерживаются самые последние версии.
С другой стороны, пользователь уже не привязан к своему основному рабочему месту, в любом Internet-кафе можно открыть нужную web-страницу и начать работу с привычными программами. И все это без каких-либо опасений подцепить вирус. Разработчиков очень заинтересовало, что их программы через день после выпуска могут увидеть пользователи всего мира, независимо от того, какой компьютер, операционную систему и браузер они используют. Хотя браузер на стороне клиента должен поддерживать Java, как уже говорилось, пользователям предлагался HotJava, доступный на любой платформе. Самый популярный в то время браузер Netscape Navigator, начиная с версии 2.0, также поддерживал Java. Однако сегодня, как известно, самый распространенный браузер - Microsoft Internet Explorer.
Компания Microsoft, добившись ошеломляющего успеха в области программного обеспечения для персональных компьютеров, стала (и в целом остается до сих пор) основным конкурентом в этой области для Sun, IBM, Netscape и других. Если в начале девяностых основные усилия Microsoft были направлены на операционную систему Windows и офисные приложения (MS Office), то в середине десятилетия стало очевидно, что пора всерьез заняться Internet. В начале 1995 года Билл Гейтс опубликовал "планы объявления войны" Netscape с целью занять такое же монопольное положение в WWW, как и в области операционных систем для персональных компьютеров. И когда вскоре Netscape подписала лицензионное соглашение с Sun, Microsoft оказалась в трудной ситуации.
Internet Explorer 2.0 не вызывал энтузиазма и никто не верил, что он может составить хоть сколько-нибудь заметную конкуренцию Netscape Navigator. А это значит, что новая версия IE 3.0 должна уметь все, что умеет только что вышедший NN 2.0. Поэтому 7 декабря 1995 года Microsoft объявляет о своем намерении лицензировать Java, а в марте 1996 года соглашение о лицензировании было подписано. Самая крупная компания по производству программного обеспечения была вынуждена поддерживать своего, возможно, самого опасного конкурента.
Сейчас мы имеем возможность оглянуться назад и оценить последствия произошедших событий. Теперь уже очевидно, что Microsoft полностью удалось осуществить свой план. Если Netscape Navigator 3.x еще сохранял лидирующее положение, то Netscape 4.x уже начал уступать Internet Explorer 4.x. Версия NN 5.x так и не вышла, а NN 6.x стал очередным разочарованием для бывших поклонников "Навигатора". Сейчас появилась версия 7.0, однако она не занимает значительной доли рынка, в то время как Internet Explorer 5.0, 5.5 и 6.0 используют более 95% пользователей.
Забавно, что многие ожесточенно обвиняли Microsoft в том, что компания боролась с Netscape "нерыночными" средствами. Однако сравним действия конкурентов. Среди многих шагов, предпринятых Microsoft, была и поддержка независимой организации W3C, которая руководила разработкой нового стандарта HTML 3. Вначале компания Netscape считалась локомотивом индустрии, поскольку она постоянно развивала и модернизировала HTML, который изначально вообще-то не предназначался для графического оформления текста. Но Microsoft, вложив большое количество средств и людских ресурсов, смогла утвердить стандарты, которые отличались от уже реализованных в Netscape Navigator, причем отличия порой были чисто формальными. В результате оказалось, что страницы, созданные в соответствии с W3C-спецификациями, отображались в Navigator искаженно. Немаловажно и то, что NN необходимо было скачивать (пусть и бесплатно) и устанавливать вручную, а IE быстро стал встроенным компонентом Windows, готовым к использованию (и от которого, кстати, избавиться нельзя было принципиально).
А каким образом Netscape смог добиться лидирующего положения? В свое время подобными же методами компания пыталась (успешно, в конце концов) вытеснить с рынка NCSA Mosaic. Тогда HTML был не особенно богат интересными возможностями, а потому инновации, поддерживаемые Navigator, сразу привлекали внимание разработчиков и пользователей. Однако такие страницы некорректно отображались в Mosaic, что заставляло его пользователей задуматься о переходе к продуктам Netscape.
В результате в связи с забвением Netscape и его Navigator многие вздохнули с облегчением. Хотя, безусловно, потеря конкуренции на рынке и воцарение такого опасного монополиста, как Microsoft, никогда не идет на пользу конечным пользователям, однако многие устали от "войны стандартов", когда и без того небогатые возможности HTML приходилось изощренно подгонять таким образом, чтобы страницы выглядели одинаково в обоих браузерах.
Про HotJava, к сожалению, сказать особенно нечего. Некоторое время Sun поддерживала этот продукт и добавила возможность визуально генерировать web-страницы без знания HTML. Однако создать конкурентоспособный браузер не удалось и вскоре развитие HotJava было остановлено. Сейчас еще можно скачать и посмотреть последнюю версию 3.0.
И последнее, на чем стоит остановиться,- это язык Java Script, который также весьма распространен и который до сих пор многие связывают с Java, видимо, по причине схожести имен. Впрочем, некоторые общие черты у них действительно есть.
4 декабря 1995 года компании Netscape и Sun совместно объявляют новый "язык сценариев" (scripting language) Java Script. Как следует из пресс-релиза, это открытый кроссплатформенный объектный язык сценариев для корпоративных сетей и Internet. Код Java Script описывается прямо в HTML-тексте (хотя можно и подгружать его из отдельных файлов с расширением .js). Этот язык предназначен для создания приложений, которые связывают объекты и ресурсы на клиентской машине или на сервере. Таким образом, Java Script, с одной стороны, расширяет и дополняет HTML, а с другой стороны - дополняет Java. С помощью Java пишутся объекты-апплеты, которыми можно управлять через язык сценариев.
Общие свойства Java Script и Java:
  • легкость в освоении. По этому параметру Java Script сравнивают с Visual Basic - чтобы использовать эти языки, опыт программирования не требуется;


  • кроссплатформенность. Код Java Script выполняется браузером. Подразумевается, что браузеры на разных платформах должны обеспечивать одинаковую функциональность для страниц, использующих язык сценариев. Однако это выполняется примерно в той же степени, что и поддержка самого HTML,- различий все же очень много;
  • открытость; спецификация языка открыта для использования и обсуждения сообществом разработчиков;
  • все перечисленные свойства позволяют утверждать, что Java Script хорошо приспособлен для Internet-программирования;
  • синтаксисы языков Java Script и Java очень похожи. Впрочем, они также довольно сильно напоминают язык С;
  • язык Java Script не объектно-ориентированный (хотя некоторые аспекты объектно-ориентированного подхода поддерживаются), но позволяет использовать различные объекты, предоставляемые браузером;
  • похожая история появления и развития. Оба языка были объявлены компаниями Sun и Netscape с интервалом в несколько месяцев. Вышедший вскоре после этого Netscape Navigator 2.0 поддерживал обе новые технологии. Возможно, само название Java Script было дано для того, чтобы воспользоваться популярностью Java, либо для того, чтобы еще больше расширить понятие "платформа Java". Вполне вероятно, что основную работу по разработке языка провела именно Netscape.


  • Несмотря на большое количество схожих характеристик, Java и Java Script - совершенно различные языки, и в первую очередь - по назначению. Если изначально Java позиционировался как язык для создания Internet-приложений (апплетов), то сейчас уже очевидно, что Java - это полноценный язык программирования. Что касается Java Script, то он полностью оправдывает свое название языка сценариев, оставаясь расширением HTML. Впрочем, расширением довольно мощным, так как любители этой технологии ухитряются создавать вполне серьезные приложения, такие как 3D-игры от первого лица (в сильно упрощенном режиме, естественно), хотя это скорее случай из области курьезов.

    В заключение отметим, что код Java Script, исполняющийся на клиенте, оказывается доступен всем в открытом виде, что затрудняет защиту авторских прав. С другой стороны, из-за отсутствия полноценной поддержки объявления новых типов программы со сложной функциональностью зачастую оказываются слишком запутанными для того, чтобы ими могли воспользоваться другие.

    Что такое Java?

    Что знают о Java обычные пользователи персональных компьютеров и Internet? Что говорят о нем разработчики, которые не занимаются этой технологией профессионально?
    Java широко известна как новейший объектно-ориентированный язык, легкий в изучении и позволяющий создавать программы, которые могут исполняться на любой платформе без каких-либо доработок (кроссплатформенность). Еще с Java почему-то всегда связана тема кофе (изображения логотипов, названия продуктов и т.д.). Программисты могут добавить к этому описанию, что язык похож на упрощенный С или С++ с добавлением garbage collector'а - автоматического сборщика "мусора" (механизм освобождения памяти, которая больше не используется программой). Также известно, что Java ориентирована на Internet, и самое распространенное ее применение - небольшие программы, апплеты, которые запускаются в браузере и являются частью HTML-страниц.
    Критики, в свою очередь, утверждают, что язык вовсе не так прост в применении, многие замечательные свойства лишь заявлены, а на самом деле не очень-то работают, а главное - программы на Java исполняются чрезвычайно медленно. Следовательно, это просто некая модная технология, которая только на время привлечет к себе внимание, а затем исчезнет, как и многие другие.
    Однако некоторые факты не позволяют согласиться с такой оценкой. Во-первых, со времени официального объявления Java прошло около семи с половиной лет - многовато для "просто модной технологии". Во-вторых, конференция разработчиков Java One, которая впервые была организована в 1996 году, уже через год собрала более 10000 участников и стала крупнейшей конференцией по созданию программного обеспечения в мире (каждый следующий год число участников росло примерно на 5000). Специальная программа Sun, объединяющая разработчиков Java по всему миру, Java Developer Connection, также была запущена в 1996 году, через год она насчитывала более 100.000 разработчиков, а в 2000 году - более 1,5 миллионов. На сегодня число программистов на Java оценивается в 3 миллиона.
    Было выпущено пять основных версий языка, начиная с 1.0 в 1995 году и заканчивая 1.4 в феврале 2002 года. Выпуск следующей версии 1.5 запланирован на 2004 год. Все версии и документацию к ним всегда можно было бесплатно получить на официальном web-сайте Java http://java.sun.com/. Один из первых продуктов для Java - JDK 1.1 (средство разработки на Java) - в течение первых трех недель после объявления был загружен более 220.000 раз. Версия 1.4 была загружена более 2 миллионов раз за первые 5 месяцев. Практически все ведущие производители программного обеспечения лицензировали технологию Java и регулярно объявляют о выходе построенных на ней продуктов. Это и "голубой гигант" IBM, и создатель платформы Macintosh фирма Apple, и лидер в области реляционных БД Oracle, и даже главный конкурент фирмы Sun - корпорация Microsoft - лицензировала Java еще в марте 1996 года.
    В следующем разделе описывается краткая история зарождения и развития идей, приведших к появлению Java, что поможет понять, чем на самом деле является эта технология, каковы ее свойства и отличительные черты, для чего она предназначена и откуда взялось такое разнообразие мнений о ней.

    Документация и литература

    На прилавках книжных магазинов вы можете найти несколько отечественных и переводных книг, посвященных программированию на языке Java. Большинство из них ориентировано на инструментарий JDK, созданный фирмой Sun, и содержат более или менее подробное описание классов Java.
    Среди удачных назовем книгу Стефана Дэвиса с названием "Learn Java Now", которая может служить учебником по языку Java для тех, кто никогда не программировал на С и С++.
    Среди переводных книг, которые можно встретить в продаже, отметим книгу Джона Родли "Создание JAVA-апплетов". Эта книга рассчитана на серьезных программистов, хорошо знающих язык программирования Java. Однако для тех, кто только начинает изучать язык Java, она может оказаться слишком сложной.
    Другая книга, заслуживающая внимание, это книга Криса Джамса с названием "Java". После небольшого введения, рассчитанного на начинающих, в этой книге приводится описание более чем дюжины достаточно интересных аплетов с исходными текстами и комментариями.
    Через сеть Internet вам доступны не только бесплатные средства разработки приложений и аплетов Java, но и грандиозные запасы документации и примеров программ.
    В качестве отправной точки для поиска вы можете выбрать сервер разработчика этого языка - фирмы Sun Microsystems, расположенный по адресу http://www.sun.com. Помимо документации и примеров программ на Java, здесь вы найдете ссылки на другие ресурсы, посвященные этому языку программирования. Попробуйте также воспользоваться поисковыми серверами, такими как Yahoo! и Alta Vista, указав в качестве ключевого слово "Java".
    Обращаем ваше внимание на cловарь Java-терминов, подготовленный Sun Microsystems специально для российских разработчиков приложений Java.
    Вы также можете ознакомиться с примерами приложений Java, которые находятся на российском сервере Sun Microsystems.

    Другие встроенные классы

    Среди других встроенных классов отметим класс Math, предназначенный для выполнения математических операций, таких как вычисление синуса, косинуса и тангенса.
    Предусмотрены также классы для выполнения запуска процессов и потоков, управления системой безопасности, а также для решения прочих системных задач.
    Библиотека встроенных классов содержит очень важные классы для работы с исключениями. Эти классы нужны для обработки ошибочных ситуаций, которые могут возникнуть (и возникают!) при работе приложений или аплетов Java.

    Интегрированные средства разработки

    Для тех, кто привык пользоваться визуальными средствами разработки, доступны два других инструмента, созданных в Sun Microsystems. Это Java WorkShop 2.0 и Java Studio 1.0.
    Интегрированная система разработки приложений Java WorkShop 2.0 содержит традиционные и визуальные средства программирования, отладчик, обширную справочную систему по классам и языку Java. С помощью Java WorkShop 2.0 вы можете создавать автономные приложения Java, аплеты, компоненты JavaBeans и собственные библиотеки классов.
    На рис. 1 мы показали внешний вид главного окна приложения Java WorkShop 2.0.

    Интегрированные средства разработки Рис. 1. Главное окно приложения .
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    Среди основных возможностей Java WorkShop 2.0 назовем синтаксическое выделение в окне редактирования, возможность удаленной отладки приложений Java, быстродействующий компилятор, а также способность работать на различных платформах.
    Разработку элементов пользовательского интерфейса можно выполнять с помощью удобного средства визуального проектирования (рис. 2).

    Интегрированные средства разработки Рис. 2. Java WorkShop 2.0 позволяет разрабатывать пользовательский интерфес с помощью визуальных средств.
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    Что же касается Java Studio 1.0, то эта система позволяет проектировать приложения вообще без программирования. Разработчик собирает приложения из готовых компонент, устанавливая между ними каналы передачи сообщений. Таким образом определяется логика работы приложения (рис. 3). Этот процесс чем-то напоминает сборку бытовой стереофонической системы из отдельных блоков, когда вы соединяете проводами входы и выходы усилителя, колонок, проигрывателя компакт-дисков и других устройств.

    Интегрированные средства разработки Рис. 3. Проектирование логики работы приложения.
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    В наборе компонент JavaBeans, доступных в Java Studio, есть компоненты, предназначенные для работы с ресурсами Internet, базами данных и таблицами, формами и так далее.
    Проектирование пользовательского интерфейса в Java Studio, также как и в Java WorkShop, выполняется визуально (рис. 4).

    Интегрированные средства разработки Рис. 4. Визуальное проектирование пользовательского интерфейса.
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    Подборку различных инструментальных средств, предназначенных для создания приложений Java, вы найдете по адресу http://www.sun.ru/download/download.html.

    Интерфейсы

    Интерфейсы создаются при помощи ключевого слова interface таким же образом, что и классы. Однако в отличие от последних, интерфейсы являются аналогом абстрактных базовых классов без полей данных и предназначены только для определений набора методов для решения каких-либо задач, например, добавления компонент в контейнеры, организации списков, сортировки и так далее.
    Вы можете создать свой класс на базе другого класса, указав при этом с помощью ключевого слова implements, что он реализует тот или иной интерфейс. При этом наряду с методами базового класса в созданном таким образом классе будут доступны методы, определенные в интерфейсе.

    История развития Java

    Теперь, когда за Java стояли не только несколько создателей, но еще и целая армия разработчиков, корпорация Sun имела возможность строить широкомасштабные планы развития технологии.

    История создания Java

    Если поискать в Internet историю создания Java, выясняется, что изначально язык назывался OaK ("дуб"), а работа по его созданию началась еще в 1990 году с довольно скандальной истории внутри корпорации Sun. Эти факты верны, однако на самом деле все было еще интереснее.

    Java выходит в свет

    Наконец, вся подготовительная работа стала подходить к своему логическому завершению. Официальное объявление Java, уже получившей широкое признание и подающей большие надежды, должно было произойти на конференции SunWorld. Ожидалось, что это будет короткое информационное объявление, так как главная цель этого мероприятия - UNIX-системы. Однако все произошло не так, как планировалось.
    В четыре часа утра в день конференции, после длинных и сложных переговоров, Sun подписывает важнейшее соглашение. Вторая сторона - компания Netscape, основанная в апреле 1994 года Джеймсом Кларком (он уже сыграл роль в судьбе OaK два года назад, когда перехватил предложение от Time Warner) и Марком Андриссеном (создателем NCSA Mosaic). Эта компания стала лидером рынка браузеров после того, как в декабре 1994 года вышла первая версия Netscape Navigator, которая была открыта для бесплатного некоммерческого использования, что позволило занять на тот момент 75% рынка.
    23 мая 1995 года технологии Java и HotJava были официально объявлены Sun и тогда же представители компании сообщили, что новая версия самого популярного браузера Netscape Navigator 2.0 будет поддерживать новую технологию. По сути, это означало, что отныне Java становится такой же неотъемлемой частью WWW, как и HTML. Уже второй раз презентация закончилась овацией - победное шествие Java началось.

    Класс String

    Класс String предназначен для работы с такими часто встречающимися объектами, как текстовые строки. Методы этого класса позволяют выполнять над строками практически все операции, которые вы делали раньше при помощи библиотечных функций C. Это преобразование строки в число и обратно с любым заданным основанием, определение длины строки, сравнение строк, извлечение подстроки и так далее.
    Хотя в языке Java не допускается перезагрузка (переопределение) операторов, для объектов класса String и объектов всех произошедших от него классов сделана встроенная перезагрузка операторов "+" и "+=". С помощью этих операторов можно выполнять слияние текстовых строк, например:
    System.out.println("x = " + x + '\n');
    Здесь в качестве параметра функции println передается текстовая строка, составленная из трех компонент: строки "x = ", числа x и символа перехода на следующую строку '\n'. Значение переменной x автоматически преобразуется в текстовую строку (что выполняется только для текстовых строк) и полученная таким образом текстовая строка сливается со строкой "x= ".

    Компания FirstPerson

    Крупные компании-производители, такие как Mitsubishi Electric, France Telecom, Dolby Labs, заинтересовались новой технологией, начались переговоры. Шеридан подготовил бизнес-план с оригинальным названием "Beyond the Green Door" ("За зеленой дверью"), в котором предложил Sun учредить дочернюю компанию для продвижения платформы OaK на рынок. 1 ноября 1992 года создается компания FirstPerson, которую возглавила Вэйн Роузинг (Wayne Rosing), перешедшая из Sun Labs. Арендуется роскошный офис, число сотрудников возрастает с 14 до 60 человек.
    Однако позднее оказалось, что стоимость подобного решения (процессор, память, экран) составляет не менее $50. Производители же бытовой техники не привыкли платить значительные суммы за дополнительную функциональность, облегчающую использование их продуктов.
    В это время внимание компьютерной индустрии захватывает идея интерактивного телевидения, создается ощущение, что именно оно станет следующим революционным прорывом. Поэтому, когда в марте 1993 года Time Warner объявляет конкурс для производителей компьютерных приставок к телевизору для развертывания пробной сети интерактивного телевидения, FirstPerson полностью переключается на эту задачу. И снова неудача - победителем оказывается Джеймс Кларк (James Clark), основатель Silicon Graphics Inc., несмотря на то, что технологически его предложение уступает OaK. Впрочем, через год проект Time Warner и SGI проваливается, а Джеймс Кларк создает компанию Netscape, которая еще сыграет важную роль в успехе Java.
    Другим потенциальным клиентом стал производитель игровых приставок 3DO. Понадобилось всего 10 дней, чтобы портировать OaK на эту платформу, однако после трехмесячных переговоров директор 3DO потребовал полные права на новый продукт, и сделка не состоялась.
    Наконец, в начале 1994 года стало понятно, что идея интерактивного телевидения оказалась нежизнеспособной. Ожиданиям не суждено было стать реальностью. Анализ состояния FirstPerson показал, что компания не имеет ни одного клиента или партнера и ее дальнейшие перспективы довольно туманны. Руководство Sun требует немедленного составления нового бизнес-плана, позволяющего компании снова приносить прибыль.

    Массивы в Java

    Назад Вперед
    Для создания массива вы можете пользоваться квадратными скобками, расположив их справа от имени массива или от типа объектов, из которых составлен массив, например:
    int nNumbers[]; int[] nAnotherNumbers;
    Допустимы оба варианта, поэтому вы можете выбрать тот, который вам больше нравится.
    При определении массивов в языке Java нельзя указывать их размер. Приведенные выше две строки не вызывают резервирования памяти для массива. Здесь просто создаются ссылки на массивы, которые без инициализации использовать нельзя.
    Для того чтобы заказать память для массива, вы должны создать соответствующие объекты с помощью ключевого слова new, например:
    int[] nAnotherNumbers; nAnotherNumbers = new int[15];
    Как выполнить инициализацию ячеек таблицы?
    Такую инициализацию можно выполнить либо статически, либо динамически. В первом случае вы просто перечисляете значения в фигурных скобках, как это показано ниже:
    int[] nColorRed = {255, 255, 100, 0, 10};
    Динамическая инициализация выполняется с использованием индекса массива, например, в цикле:
    int nInitialValue = 7; int[] nAnotherNumbers; nAnotherNumbers = new int[15]; for(int i = 0; i < 15; i++) { nAnotherNumbers[i] = nInitialValue; }
    Вы можете создавать массивы не только из переменных базовых типов, но и из произвольных объектов. Каждый элемент такого массива должен инициализироваться оператором new.
    Массивы могут быть многомерными и, что интересно, несимметричными.
    Ниже создается массив массивов. В нулевом и первом элементе создается массив из четырех чисел, а во втором - из восьми:
    int[][] nDim = new int[5][10]; nDim[0] = new int [4]; nDim[1] = new int [4]; nDim[2] = new int [8];
    Заметим, что во время выполнения приложения виртуальная машина Java проверяет выход за границы массива. Если приложение пытается выйти за границы массива, происходит исключение.
    Массивы в языке Java являются объектами некоторого встроенного класса. Для этого класса существует возможность определить размер массива, обратившись к элементу данных класса с именем length, например:

    int[] nAnotherNumbers; nAnotherNumbers = new int[15]; for(int i = 0; i < nAnotherNumbers.length; i++) { nAnotherNumbers[i] = nInitialValue; }

    Для определения размера массива вам не нужен такой оператор, как sizeof из языка программирования С, потому что существует другой способ определения этого размера.

    Назад Вперед

    Массивы в JavaМассивы в Java Контакты

    О компании

    Новости

    Вакансии

    Правовые аспекты

    Условия использования

    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.

    Массивы в Java

    printmenus();

    Массивы в JavaПрограммные продукты

    Рабочие станции и тонкие клиенты

    Серверы

    Системы хранения данных

    Посмотреть все Массивы в Java»

    Массивы в JavaSolaris 10

    Java 2 Standard Edition

    Developer Tools

    Top Downloads

    New Downloads

    Патчи и обновления

    Посмотреть все Массивы в Java»

    Массивы в JavaКаталог решений

    Истории успеха

    The Sun Grid

    Партнерские программы

    Посмотреть все Массивы в Java»

    Массивы в JavaГарантийное обслуживание

    Программы SunSpectrum

    Консалтинг

    Услуги инсталляции

    Поддержка ПО

    Посмотреть все Массивы в Java»

    Массивы в JavaОписание курсов

    Сертификация

    Авторизованные учебные центры

    Посмотреть все Массивы в Java»

    Массивы в JavaПроекты

    События

    Lab Downloads

    Посмотреть все Массивы в Java»

    Массивы в Java

    Мобильность Java

    Назад Вперед
    В свое время вы слышали, что язык программирования С является мобильным. Это нужно понимать в том смысле, что имеется принципиальная возможность переноса программ C на различные платформы.
    Однако следует отметить, что создание приложений, действительно работающих на разных платформах - непростая задача. К сожалению, дело не ограничивается необходимостью перекомпиляции исходного текста программы для работы в другой среде. Много проблем возникает с несовместимостью программных интерфейсов различных операционных систем и графических оболочек, реализующих пользовательский интерфейс.
    Вспомните хотя бы проблемы, связанные с переносом 16-разрядных приложений Windows в 32-разрядную среду Windows 95 и Windows NT. Даже если вы тщательно следовали всем рекомендациям, разрабатывая приложения так, чтобы они могли работать в будущих версиях Windows, едва ли вам удастся просто перекомпилировать исходные тексты, не изменив в них ни строчки. Ситуация еще больше ухудшается, если вам нужно, например, перенести исходные тексты приложения Windows в среду операционной системы OS/2 или в оболочку X-Windows операционной системы UNIX. А ведь есть еще другие компьютеры и рабочие станции!
    Как нетрудно заметить, даже если стандартизовать язык программирования для всех платформ, проблемы совместимости с программным интерфейсом операционной системы значительно усложняют перенос программ на различные платформы. И, конечно, вы не можете мечтать о том, чтобы загрузочный модуль одной и той же программы мог работать без изменений в среде различных операционных систем и на различных платформах. Если программа подготовлена для процессора Intel, она ни за что не согласится работать на процессоре Alpha или каком-либо другом.
    В результате создавая приложение, способное работать на различных платформах, вы вынуждены фактически делать несколько различных приложений и сопровождать их по отдельности.
    На рис. 5 мы показали, как приложение, изначально разработанное для Windows NT, переносится на платформу Apple Macintosh.

    Мобильность Java

    Рис. 5. Перенос приложения с платформы Windows NT на платформу Macintosh

    Вначале программист готовит исходные тексты приложения для платформы Windows NT и отлаживает их там. Для получения загрузочного модуля исходные тексты компилируются и редактируются. Полученный в результате загрузочный модуль может работать на процессоре фирмы Intel в среде операционной системы Windows NT.

    Для того чтобы перенести приложение в среду операционной системы компьютера Macintosh, программист вносит необходимые изменения в исходные тексты приложения. Эти изменения необходимы из-за различий в программном интерфейсе операционной системы Windows NT и операционной системы, установленной в Macintosh. Далее эти исходные тексты транслируются и редактируются, в результате чего получается загрузочный модуль, способный работать в среде Macintosh, но не способный работать в среде Windows NT.

    Программа на языке Java компилируется в двоичный модуль, состоящий из команд виртуального процессора Java. Такой модуль содержит байт-код, предназначенный для выполнения Java-интерпретатором. На настоящий момент уже созданы первые модели физического процессора, способного выполнять этот байт-код, однако интерпретаторы Java имеются на всех основных компьютерных платформах. Разумеется, на каждой платформе используется свой интерпретатор, или, точнее говоря, свой виртуальный процессор Java.

    Если ваше приложение Java (или аплет) должно работать на нескольких платформах, нет необходимости компилировать его исходные тексты несколько раз. Вы можете откомпилировать и отладить приложение Java на одной, наиболее удобной для вас платформе. В результате вы получите байт-код, пригодный для любой платформы, где есть виртуальный процессор Java.

    Сказанное иллюстрируется на рис. 6.

    Мобильность Java

    Рис. 6. Подготовка приложения Java для работы на разных платформах

    Таким образом, приложение Java компилируется и отлаживается только один раз, что уже значительно лучше. Остается, правда, вопрос - как быть с программным интерфейсом операционной системы, который отличается для разных платформ?

    Здесь, на наш взгляд, разработчиками Java предлагается достаточно неплохое решение. Приложение Java не обращается напрямую к интерфейсу операционной системы. Вместо этого оно пользуется готовыми стандартными библиотеками классов, содержащими все необходимое для организации пользовательского интерфейса, обращения к файлам, для работы в сети и так далее.

    Внутренняя реализация библиотек классов, разумеется, зависит от платформы. Однако все загрузочные модули, реализующие возможности этих библиотек, поставляются в готовом виде вместе с виртуальной машиной Java, поэтому программисту не нужно об этом заботиться. Для операционной системы Windows, например, поставляются библиотеки динамической загрузки DLL, внутри которых запрятана вся функциональность стандартных классов Java.

    Абстрагируясь от аппаратуры на уровне библиотек классов, программисты могут больше не заботиться о различиях в реализации программного интерфейса конкретных операционных систем. Это позволяет создавать по-настоящему мобильные приложения, не требующие при переносе на различные платформы перетрансляции и изменения исходного текста.

    Еще одна проблема, возникающая при переносе программ, составленных на языке программирования С, заключается в том, что размер области памяти, занимаемой переменными стандартных типов, различный на разных платформах. Например, в среде операционной системы Windows версии 3.1 переменная типа int в программе, составленной на С, занимает 16 бит. В среде Windows NT этот размер составляет 32 бита.

    Очевидно, что трудно составлять программу, не зная точно, сколько имеется бит в слове или в байте. При переносе программ на платформы с иной разрядностью могут возникать ошибки, которые трудно обнаружить.

    В языке Java все базовые типы данных имеют фиксированную разрядность, которая не зависит от платформы. Поэтому программисты всегда знают размеры переменных в своей программе.


    Copyright 1994- 2005 Sun Microsystems, Inc.

    Мобильность Java

    printmenus();

    Мобильность JavaПрограммные продукты

    Рабочие станции и тонкие клиенты

    Серверы

    Системы хранения данных

    Посмотреть все Мобильность Java»

    Мобильность JavaSolaris 10

    Java 2 Standard Edition

    Developer Tools

    Top Downloads

    New Downloads

    Патчи и обновления

    Посмотреть все Мобильность Java»

    Мобильность JavaКаталог решений

    Истории успеха

    The Sun Grid

    Партнерские программы

    Посмотреть все Мобильность Java»

    Мобильность JavaГарантийное обслуживание

    Программы SunSpectrum

    Консалтинг

    Услуги инсталляции

    Поддержка ПО

    Посмотреть все Мобильность Java»

    Мобильность JavaОписание курсов

    Сертификация

    Авторизованные учебные центры

    Посмотреть все Мобильность Java»

    Мобильность JavaПроекты

    События

    Lab Downloads

    Посмотреть все Мобильность Java»

    Мобильность Java

    Наследование

    С помощью ключевого слова extends вы можете унаследовать один класс (дочерний) от другого (базового).
    Множественное наследование не допускается. Таким образом, для каждого дочернего класса может быть только один базовый класс. При необходимости, однако, этот дочерний класс может реализовывать произвольное количество интерфейсов.
    Для ссылки на методы базового класса вы должны использовать ключевое слово super.
    При необходимости вы можете вызвать в первой исполняемой строке конструктора дочернего класса конструктор базового класса (опять же с помощью ключевого слова super).
    Назад Вперед

    НаследованиеНаследование Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Наследование
    printmenus();
    НаследованиеПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Наследование»


    НаследованиеSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Наследование»


    НаследованиеКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Наследование»


    НаследованиеГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Наследование»


    НаследованиеОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Наследование»


    НаследованиеПроекты
    События
    Lab Downloads
    Посмотреть все Наследование»


    Наследование

    Определение класса

    Для создания классов вы можете использовать только ключевое слово class. Что же касается union, то это ключевое слово в Java не применяется.
    В языке программирования С++ описание класса может быть отделено от его определения. Для Java это не так - описание класса недопустимо. Все методы должны быть определены внутри определения класса.
    Недопустимо определение вложенных классов.
    В Java также нет шаблонов. Вы можете создавать классы только на базе других классов.
    Объект класса создается при помощи ключевого слова new, однако вы не можете удалить объект явным образом, так как ключевое слово delete языка программирования С++ в Java не используется.
    При определении класса вы не можете указать деструктор. Функции удаления объектов Java из памяти выполняет система сборки мусора.
    Внутри одного исходного файла вы можете определить только один общедоступный класс public.
    Все классы в Java наследуются от класса Object, поэтому для любого объекта вы можете использовать методы этого класса.

    Определение методов

    Вы не можете определять методы вне тела класса, создавая таким образом глобальные функции. Нет также возможности определения вне класса глобальных данных. Тем не менее, внутри класса можно определять статические методы и поля (с помощью ключевого слова static), которые будут играть роль глобальных методов и данных.
    Пользуясь ключевыми словами static и final, можно определять внутри классов глобальные константы.
    Если в базовом классе метод определен с ключевым словом final, его нельзя переопределить в дочернем классе, созданном на базе данного метода.
    Методы не могут быть определены как inline.
    Методы Java могут создавать исключения, вызванные возникновением ошибок или других событий. Все исключения должны либо обрабатываться внутри метода, либо описываться в определении метода после ключевого слова throws.

    Основные версии и продукты Java

    Сразу оговоримся, что под продуктами здесь понимаются программные решения от компании Sun, являющиеся "образцами реализации" (reference implementation).
    Итак, впервые Java была объявлена 23 мая 1995 года. Основными продуктами, доступными на тот момент в виде бета-версий, были:

  • Java language specification, JLS, спецификация языка Java (описывающая лексику, типы данных, основные конструкции и т.д.);
  • спецификация JVM;

  • Java Development Kit, JDK - средство разработчика, состоящее в основном из утилит, стандартных библиотек классов и демонстрационных примеров.

  • Спецификация языка была составлена настолько удачно, что практически без изменений используется и по сей день. Конечно, было внесено большое количество уточнений, более подробных описаний, были добавлены и некоторые новые возможности (например, объявление внутренних классов), однако основные концепции остаются неизменными. Данный курс в большой степени опирается именно на спецификацию языка.
    Спецификация JVM предназначена в первую очередь для создателей виртуальных машин, а потому практически не используется Java-программистами.
    JDK долгое время было базовым средством разработки приложений. Оно не содержит никаких текстовых редакторов, а оперирует только уже существующими Java-файлами. Компилятор представлен утилитой javac (java compiler) . Виртуальная машина реализована программой java . Для тестовых запусков апплетов существует специальная утилита appletviewer. Наконец, для автоматической генерации документации на основе исходного кода прилагается средство javadoc.
    Первая версия содержала всего 8 стандартных библиотек:
  • java.lang - базовые классы, необходимые для работы любого приложения (название - сокращение от language);
  • java.util - многие полезные вспомогательные классы;
  • java.applet - классы для создания апплетов;
  • java.awt, java.awt.peer - библиотека для создания графического интерфейса пользователя (GUI), называется Abstract Window Toolkit, AWT, подробно описывается в лекции 11;
  • java.awt.image - дополнительные классы для работы с изображениями;
  • java.io - работа с потоками данных (streams) и с файлами;
  • java.net - работа с сетью.


  • Таким образом, все библиотеки начинаются с java, именно они являются стандартными. Все остальные (начинающиеся с com, org и др.) могут меняться в любой версии без поддержки совместимости.

    Финальная версия JDK 1.0 была выпущена в январе 1996 года.

    Сразу поясним систему именования версий. Обозначение версии состоит из трех цифр. Первой пока всегда стоит 1. Это означает, что поддерживается полная совместимость между всеми версиями 1.х.х. То есть программа, написанная на более старом JDK, всегда успешно выполнится на более новом. По возможности соблюдается и обратная совместимость - если программа откомпилирована более новым JDK, а никакие новые библиотеки не использовались, то в большинстве случаев старые виртуальные машины смогут выполнить такой код.

    Вторая цифра изменилась от 0 до 4 (последняя на момент создания курса). В каждой версии происходило существенное расширение стандартных библиотек (212, 504, 1781, 2130 и 2738 - количество классов и интерфейсов с 1.0 по 1.4), а также добавлялись некоторые новые возможности в сам язык. Менялись и утилиты, входящие в JDK.

    Наконец, третья цифра означает развитие одной версии. В языке или библиотеках ничего не меняется, лишь устраняются ошибки, производится оптимизация, могут меняться (добавляться) аргументы утилит. Так, последняя версия JDK 1.0 - 1.0.2.

    Хотя с развитием версии 1.х ничего не удаляется, конечно, какие-то функции или классы устаревают. Они объявляются deprecated, и хотя они будут поддерживаться до объявления 2.0 (а про нее пока ничего не было слышно), пользоваться ими не рекомендуется.

    Вместе с первым успехом JDK 1.0 подоспела и критика. Основные недостатки, обнаруженные разработчиками, были следующими. Во-первых, конечно, производительность. Первая виртуальная машина работала очень медленно. Это связано с тем, что JVM, по сути, представляет собой интерпретатор, который работает всегда медленнее, чем исполняется откомпилированный код. Однако успешная оптимизация, устранившая этот недостаток, была еще впереди. Также отмечались довольно бедные возможности AWT, отсутствие работы с базами данных и другие.

    В декабре 1996 года объявляется новая версия JDK 1.1, сразу выкладывается для свободного доступа бета-версия. В феврале 1997 года выходит финальная версия. Что было добавлено в новом выпуске Java?

    Конечно, особое внимание было уделено производительности. Многие части виртуальной машины были оптимизированы и переписаны с использованием Assembler, а не C, как до этого. Кроме того, с октября 1996 года Sun развивает новый продукт - Just-In-Time компилятор, JIT. Его задача - транслировать Java байт-код программы в "родной" код операционной системы. Таким образом, время запуска программы увеличивается, но зато выполнение может ускоряться в некоторых случаях до 50 раз! С июля 1997 года появляется реализация под Windows и JIT стандартно входит в JDK с возможностью отключения.

    Были добавлены многие новые важные возможности. JavaBeans- технология, объявленная еще в 1996 году, позволяет создавать визуальные компоненты, которые легко интегрируются в визуальные средства разработки. JDBC (Java DataBase Connectivity) обеспечивает доступ к базам данных. RMI (Remote Method Invocation) позволяет легко создавать распределенные приложения. Были усовершенствованы поддержка национальных языков и система безопасности.

    За первые три недели JDK 1.1 был скачан более 220.000 раз, менее чем через год - более двух миллионов раз. На данный момент версия 1.1 считается полностью устаревшей и ее развитие остановилось на 1.1.8. Однако из-за того, что самый распространенный браузер MS IE до сих пор поддерживает только эту версию, она продолжает использоваться для написания небольших апплетов.

    Кроме того, с 11 марта 1997 года компания Sun начала предлагать Java Runtime Environment, JRE (среду выполнения Java). По сути дела, это минимальная реализация виртуальной машины, необходимая для исполнения Java-приложений, без компилятора и других средств разработки. Если пользователь хочет только запускать программы, это именно то, что ему нужно.

    Как видно, самым главным недостатком осталась слабая поддержка графического интерфейса пользователя (GUI). В декабре 1996 года компании Sun и Netscape объявляют новую библиотеку IFC (Internet Foundation Classes), разработанную Netscape полностью на Java и предназначенную как раз для создания сложного оконного интерфейса. В апреле 1997 года объявляется, что компании планируют объединить технологии AWT от Sun и IFC от Netscape для создания нового продукта Java Foundation Classes, JFC, в который должны войти:

  • усовершенствованный оконный интерфейс, который получил особое название - Swing ;
  • реализация Drag-and-Drop;
  • поддержка 2D-графики, более удобная работа с изображениями;
  • Accessibility API для пользователей с ограниченными возможностями



  • и другие функции. Компания IBM также поддержала разработку новой технологии. В июле 1997 года стала доступна первая версия JFC. Первоначально библиотеки назывались, например, com.sun.java.swing для компонентов Swing . В марте 1998 года вышла финальная версия этой технологии. За полгода продукт был скачан более 500.000 раз.

    Выход следующей версии Java 1.2 много раз откладывался, но в итоге она настолько превзошла предыдущую 1.1, что ее и все последующие версии начали называть платформой Java 2 (хотя номера, конечно, по-прежнему отсчитывались как 1.х.х, см. выше описание правил нумерации). Первая бета-версия стала доступной в декабре 1997 года, а финальная версия была выпущена 8 декабря 1998 года, и за первые восемь месяцев ее скачали более миллиона раз.

    Список появившихся возможностей очень широк, поэтому перечислим наиболее значимые из них:

  • существенно переработанная модель безопасности, введены понятия политики (policy) и разрешения (permission);


  • JFC стал стандартной частью JDK, причем библиотеки стали называться, например, javax.swing для Swing (название javax указывает, что до этого библиотека считалась расширением Java);
  • полностью переработанная библиотека коллекций (collection framework) - классов для хранения набора объектов;


  • Java Plug-in был включен в JDK;
  • улучшения в производительности, глобализации (независимости от особенностей разных платформ и стран), защита от "проблемы-2000".


  • С февраля 1999 года исходный код самой JVM был открыт для бесплатного доступа всем желающим.

    Самое же существенное изменение произошло 15 июня 1999 года, спустя полгода после выхода JDK 1.2. На конференции разработчиков JavaOne компания Sun объявила о разделении развития платформы Java 2 на три направления:



  • Java 2 Platform, Standard Edition ( J2SE);


  • Java 2 Platform, Enterprise Edition ( J2EE);


  • Java 2 Platform, Micro Edition ( J2ME).


  • На самом деле, подобная классификация уже давно назрела, в частности, различных спецификаций и библиотек насчитывалось несколько десятков, а потому они нуждались в четкой структуризации. Кроме того, такое разделение облегчало развитие и продвижение на рынок технологии Java.


    J2SE предназначается для использования на рабочих станциях и персональных компьютерах. Standard Edition - основа технологии Java и прямое развитие JDK (средство разработчика было переименовано в j2sdk).

    J2EE содержит все необходимое для создания сложных, высоконадежных, распределенных серверных приложений. Условно можно сказать, что Enterprise Edition - это набор мощных библиотек (например, Enterprise Java Beans, EJB) и пример реализации платформы (сервера приложений, Application Server), которая их поддерживает. Работа такой платформы всегда опирается на j2sdk.

    J2ME является усечением Standard Edition, чтобы удовлетворять жестким аппаратным требованиям небольших устройств, таких как карманные компьютеры и сотовые телефоны.

    Далее развитие этих технологий происходит разными темпами. Если J2SE уже была доступна более полугода, то финальная версия J2EE вышла лишь в декабре 1999 года. Последняя версия j2sdk 1.2 на данный момент - 1.2.2.

    Тем временем борьба за производительность продолжалась, и Sun пытался еще больше оптимизировать виртуальную машину. В марте 1999 года объявляется новый продукт - высокоскоростная платформа (engine) Java HotSpot. Была оптимизирована работа с потоками исполнения, существенно переработаны алгоритмы автоматического сборщика мусора (garbage collector) и многое другое. Ускорение действительно было очень существенным, всегда заметное невооруженным взглядом за несколько минут работы с Java-приложением.

    Новая платформа может работать в двух режимах - клиентском и серверном. Режимы различались настройками и другими оптимизирующими алгоритмами. По умолчанию работа идет в клиентском режиме.

    Развитие HotSpot продолжалось более года, пока в начале мая 2000 года высокопроизводительная JVM не вошла в состав новой версии J2SE. В эту версию было внесено еще множество улучшений и исправлений, но именно прогресс в скорости работы стал ключевым изменением нового j2sdk 1.3 (последняя подверсия 1.3.1).

    Наконец, последняя на данный момент версия J2SE 1.4 вышла в феврале 2002 года. Она была разработана для более полной поддержки web-сервисов (web services). Поэтому основные изменения коснулись работы с XML (Extensible Markup Language). Другое революционное добавление - выражение assert, позволяющее в отладочном режиме проверять верность условий, что должно серьезно упростить разработку сложных приложений. Наконец, были добавлены классы для работы с регулярными выражениями.

    За первые пять месяцев j2sdk 1.4 было скачано более двух миллионов раз. В августе 2002 года уже была предложена версия 1.4.1, остающаяся на данный момент самой современной.

    В заключение для демонстрации уровня развития Standard Edition приведем стандартные диаграммы, описывающие все составляющие технологии, из документации к версиям 1.3 и 1.4.

    Основные версии и продукты Java
    1.1.Составляющие технологии версии 1.3.

    Основные версии и продукты Java
    1.2. Составляющие технологии версии 1.4.

    Особенности реализации классов в Java

    Назад Вперед
    Если вы умеете программировать на языке С++, у вас не возникнет никаких проблем с программированием на Java, так как эти языки очень похожи. Однако есть и некоторые отличия, которые следует учитывать. Мы приведем краткое перечисление основных отличий.

    Пакетные средства

    В JDK входят пакетные программы для компиляции исходных текстов приложений Java, виртуальная машина, программа автоматизированного создания документации по классам, справочник по классам Java и другие необходимые средства.

    Переопределение операторов

    В языке С++ вы могли переопределить операторы, такие как +, -, ++ и так далее. Язык Java не допускает переопределение, что сделано для упрощения программирования. Тем не менее, операторы "+" и "+=" перегружены по умолчанию для выполнения операции слияния текстовых строк класса String.

    Платформа Java

    Итак, Java обладает длинной и непростой историей развития, однако настало время рассмотреть, что же получилось у создателей, какими свойствами обладает данная технология.
    Самое широко известное, и в тоже время вызывающее самые бурные споры, свойство - много- или кроссплатформенность. Уже говорилось, что оно достигается за счет использования виртуальной машины JVM, которая является обычной программой, исполняемой операционной системой и предоставляющей Java-приложениям все необходимые возможности. Поскольку все параметры JVM специфицированы, то остается единственная задача - реализовать виртуальные машины на всех существующих и используемых платформах.
    Наличие виртуальной машины определяет многие свойства Java, однако сейчас остановимся на следующем вопросе - является Java языком компилируемым или интерпретируемым? На самом деле, используются оба подхода.
    Исходный код любой программы на языке Java представляется обычными текстовыми файлами, которые могут быть созданы в любом текстовом редакторе или специализированном средстве разработки и имеют расширение .java. Эти файлы подаются на вход Java-компилятора, который транслирует их в специальный Java байт-код. Именно этот компактный и эффективный набор инструкций поддерживается JVM и является неотъемлемой частью платформы Java.
    Результат работы компилятора сохраняется в бинарных файлах с расширением .class. Java-приложение, состоящее из таких файлов, подается на вход виртуальной машине, которая начинает их исполнять, или интерпретировать, так как сама является программой.
    Многие разработчики поначалу жестко критиковали смелый лозунг Sun "Write once, run everywhere", обнаруживая все больше и больше несоответствий и нестыковок на различных платформах. Однако надо признать, что они просто были слишком нетерпеливы. Java только появилась на свет, а первые версии спецификаций были недостаточно исчерпывающими.
    Очень скоро специалисты Sun пришли к выводу, что просто свободно публиковать спецификации (что уже делалось задолго до Java) недостаточно. Необходимо еще и создавать специальные процедуры проверки новых продуктов на соответствие стандартам. Первый такой тест для JVM содержал всего около 600 проверок, через год их число выросло до десяти тысяч и с тех пор все время увеличивается (именно его в свое время не смог пройти MS IE 4.0). Безусловно, авторы виртуальных машин все время совершенствовали их, устраняя ошибки и оптимизируя работу. Все-таки любая, даже очень хорошо задуманная технология требует времени для создания высококачественной реализации. Аналогичный путь развития сейчас проходит Java 2 Micro Edition (J2ME), но об этом позже.
    Следующим по важности свойством является объектная ориентированность Java, что всегда упоминается во всех статьях и пресс-релизах. Сам объектно-ориентированный подход (ООП) рассматривается в следующей лекции, однако важно подчеркнуть, что в Java практически все реализовано в виде объектов - потоки выполнения (threads) и потоки данных (streams), работа с сетью, работа с изображениями, с пользовательским интерфейсом , обработка ошибок и т.д. В конце концов, любое приложение на Java - это набор классов, описывающих новые типы объектов.
    Подробное рассмотрение объектной модели Java проводится на протяжении всего курса, однако обозначим основные особенности. Прежде всего, создатели отказались от множественного наследования. Было решено, что оно слишком усложняет и запутывает программы. В языке используется альтернативный подход - специальный тип "интерфейс". Он подробно рассматривается в соответствующей лекции.
    Далее, в Java применяется строгая типизация. Это означает, что любая переменная и любое выражение имеет тип, известный уже на момент компиляции. Такой подход применен для упрощения выявления проблем, ведь компилятор сразу сообщает об ошибках и указывает их расположение в коде. Поиск же исключительных ситуаций (exceptions - так в Java называются некорректные ситуации) во время исполнения программы (runtime) потребует сложного тестирования, при этом причина дефекта может обнаружиться совсем в другом классе. Таким образом, нужно прикладывать дополнительные усилия при написании кода, зато существенно повышается его надежность (а это одна из основополагающих целей, для которых и создавался новый язык).
    В Java существует всего 8 типов данных, которые не являются объектами. Они были определены с самой первой версии и никогда не менялись. Это пять целочисленных типов: byte, short, int, long, а также к ним относят символьный char. Затем два дробных типа float и double и, наконец, булевский тип boolean. Такие типы называются простые , или примитивные (от английского primitive), и они подробно рассматриваются в лекции, посвященной типам данных. Все остальные типы - объектные или ссылочные (англ. reference).
    Синтаксис Java почему-то многих ввел в заблуждение. Он действительно создан на основе синтаксиса языков C/C++, так что если посмотреть на исходный код программ, написанных на этих языках и на Java , то не сразу удается понять, какая из них на каком языке написана. Это почему-то дало многим повод думать, что Java - это упрощенный C++ с дополнительными возможностями, такими как garbage collector. Автоматический сборщик мусора (garbage collector) мы рассмотрим чуть ниже, но считать, что Java такой же язык, как и C++,- большое заблуждение.
    Конечно, разрабатывая новую технологию, авторы Java опирались на широко распространенный язык программирования по целому ряду причин. Во-первых, они сами на тот момент считали C++ своим основным инструментом. Во-вторых, зачем придумывать что-то новое, когда есть вполне подходящее старое? Наконец, очевидно, что незнакомый синтаксис отпугнет разработчиков и существенно осложнит внедрение нового языка, а ведь Java должна была максимально быстро получить широкое распространение. Поэтому синтаксис был лишь слегка упрощен, чтобы избежать слишком запутанных конструкций.
    Но, как уже говорилось, С++ принципиально не годился для новых задач, которые поставили себе разработчики из компании Sun, поэтому модель Java была построена заново, причем в соответствии с совсем другими целями. Дальнейшие лекции будут постепенно раскрывать конкретные различия.
    Что же касается объектной модели, то она скорее была построена по образцу таких языков, как Smalltalk от IBM, или разработанный еще в 60-е годы в Норвежском Вычислительном Центре язык Simula, на который ссылается сам создатель Java Джеймс Гослинг.
    Другое немаловажное свойство Java - легкость в освоении и разработке - также получило неоднозначную оценку. Действительно, авторы потрудились избавить программистов от наиболее распространенных ошибок, которые порой допускают даже опытные разработчики на C/C++. И первое место здесь занимает работа с памятью.
    В Java с самого начала был введен механизм автоматической сборки мусора (от английского garbage collector). Предположим, программа создает некоторый объект, работает с ним, а дальше наступает момент, когда он больше уже не нужен. Необходимо освободить занимаемую память, чтобы не мешать операционной системе нормально функционировать. В С/С++ это необходимо делать явным образом из программы. Очевидно, что при таком подходе существует две опасности - либо удалить объект, который еще кому-то необходим (и если к нему действительно произойдет обращение, то возникнет ошибка), либо не удалять объект, ставший ненужным, а это означает утечку памяти, то есть программа начинает потреблять все большее количество оперативной памяти.
    При разработке на Java программист вообще не думает об освобождении памяти. Виртуальная машина сама подсчитывает количество ссылок на каждый объект, и если оно становится равным нулю, то такой объект помечается для обработки garbage collector . Таким образом, программист должен следить лишь за тем, чтобы не оставалось ссылок на ненужные объекты. Сборщик мусора - это фоновый поток исполнения, который регулярно просматривает существующие объекты и удаляет уже не нужные. Из программы никак нельзя повлиять на работу garbage collector, можно только явно инициировать его очередной проход с помощью стандартной функции. Ясно, что это существенно упрощает разработку программ, особенно для начинающих программистов.
    Однако опытные разработчики были недовольны тем, что они не могут полностью контролировать все, что происходит с их системой. Нет точной информации, когда именно будет удален объект, ставший ненужным, когда начнет работать (а значит, и занимать системные ресурсы) поток сборщика мусора и т.д. Но, при всем уважении к опыту таких программистов, необходимо отметить, что подавляющее количество сбоев программ, написанных на С/С++, приходится именно на некорректную работу с памятью, причем порой это случается даже с широко распространенными продуктами весьма серьезных компаний.
    Кроме того, особый упор делался на легкость освоения новой технологии. Как уже было сказано, ожидалось (и эти ожидания оправдались, в подтверждение правильности выбранного пути!), что Java должна получить максимально широкое применение, даже в тех компаниях, где никогда до этого не занимались программированием на таком уровне (бытовая техника типа тостеров и кофеварок, создание игр и других приложений для сотовых телефонов и т.д.). Был и целый ряд других соображений. Продукты для обычных пользователей, а не профессиональных программистов, должны быть особенно надежными. Internet стал Всемирной Сетью, поскольку появились непрофессиональные пользователи, а возможность создавать апплеты для них не менее привлекательна. Им требовался простой инструмент для создания надежных приложений.
    Наконец, Internet-бум 90-х годов набирал обороты и выдвигал новые, более жесткие требования к срокам разработки. Многолетние проекты, которые были в прошлом обычным делом, перестали отвечать потребностям заказчиков, новые системы надо было создавать максимум за год, а то и за считанные месяцы.
    Кроме введения garbage collector, были предприняты и другие шаги для облегчения разработки. Некоторые из них уже упоминались - отказ от множественного наследования, упрощение синтаксиса и др. Возможность создания многопоточных приложений была реализована в первой же версии Java (исследования показали, что это очень удобно для пользователей, а существующие стандарты опираются на телетайпные системы, которые устарели много лет назад). Другие особенности будут рассмотрены в следующих лекциях. Однако то, что создание и поддержка систем действительно проще на Java, чем на C/C++, давно является общепризнанным фактом. Впрочем, все-таки эти языки созданы для разных целей, и каждый имеет свои неоспоримые преимущества.
    Следующее важное свойство Java - безопасность . Изначальная нацеленность на распределенные приложения, и в особенности решение исполнять апплеты на клиентской машине, сделали вопрос защиты одним из самых приоритетных. При работе любой виртуальной машины Java действует целый комплекс мер. Далее приводится лишь краткое описание некоторых из них.
    Во-первых, это правила работы с памятью. Уже говорилось, что очистка памяти производится автоматически. Резервирование ее также определяется JVM, а не компилятором, или явным образом из программы, разработчик может лишь указать, что он хочет создать еще один новый объект. Указатели по физическим адресам отсутствуют принципиально.
    Во-вторых, наличие виртуальной машины-интерпретатора значительно облегчает отсечение опасного кода на каждом этапе работы. Сначала байт-код загружается в систему, как правило, в виде class-файлов. JVM тщательно проверяет, все ли они подчиняются общим правилам безопасности Java и не созданы ли злоумышленниками с помощью каких-то других средств (и не искажены ли при передаче). Затем, во время исполнения программы, интерпретатор легко может проверить каждое действие на допустимость. Возможности классов, которые были загружены с локального диска или по сети, существенно различаются (пользователь легко может назначать или отменять конкретные права). Например, апплеты по умолчанию никогда не получат доступ к локальной файловой системе. Такие встроенные ограничения есть во всех стандартных библиотеках Java.
    Наконец, существует механизм подписания апплетов и других приложений, загружаемых по сети. Специальный сертификат гарантирует, что пользователь получил код именно в том виде, в каком его выпустил производитель. Это, конечно, не дает дополнительных средств защиты, но позволяет клиенту либо отказаться от работы с приложениями ненадежных производителей, либо сразу увидеть, что в программу внесены неавторизованные изменения. В худшем случае он знает, кто ответственен за причиненный ущерб.
    Совокупность описанных свойств Java позволяет утверждать, что язык весьма приспособлен для разработки Internet- и интранет (внутренние сети корпораций)-приложений.
    Наконец, важная отличительная особенность Java - это его динамичность. Язык очень удачно задуман, в его развитии участвуют сотни тысяч разработчиков и многие крупные компании. Основные этапы этого развития кратко освещены в следующем разделе.
    Итак, подведем итоги. Java-платформа обладает следующими преимуществами:
  • переносимость, или кроссплатформенность;
  • объектная ориентированность, создана эффективная объектная модель;
  • привычный синтаксис С/С++;
  • встроенная и прозрачная модель безопасности ;
  • ориентация на Internet-задачи, сетевые распределенные приложения;

  • динамичность, легкость развития и добавления новых возможностей;
  • простота освоения.

  • Но не следует считать, что более легкое освоение означает, что изучать язык не нужно вовсе. Чтобы писать действительно хорошие программы, создавать большие сложные системы, необходимо четкое понимание всех базовых концепций Java и используемых библиотек. Именно этому и посвящен данный курс.

    Подключаемые библиотеки классов

    Ниже мы кратко перечислим подключаемые библиотеки классов для того чтобы вы могли оценить возможности набора классов Java. Подробное описание этих классов есть в справочной системе Java WorkShop и в различной литературе, посвященной Java. Мы же ограничимся описанием тех классов, которые будем использовать в наших примерах приложений.

    Подключение к Internet

    Заметим, что для запуска аплетов вам не нужно обязательно подключаться к Internet - вы можете встраивать аплеты в документы HTML, расположенные на локальном диске вашего компьютера и просматривать эти документы браузером просто как локальные файлы.
    Автономные приложения Java работают под управлением специального интерпретатора (виртуальной машины Java), поэтому для их отладки вам также не потребуется сеть Internet.
    Однако есть одно важное обстоятельство - аплеты, взаимодействующие с расширениями сервера Web, должны быть загружены именно с этого сервера. В противном случае их работа будет заблокирована по соображениям безопасности.
    Если вы собираетесь проверять работу приложений и аплетов Java, взаимодействующих с сервером Web, вы можете воспользоваться собственным сервером в Internet или в корпоративной сети Intranet (если они у вас есть). Можно также установить сервер Web, входящий в комплект операционной системы Windows NT Workstation версии 4.0, или Personal Web Service для операционной системы Windows 95.

    Проект Green

    5 декабря 1990 года, в день, когда Нотон должен был перейти в компанию NeXT, Sun сделала ему встречное предложение. Руководство согласилось со всеми его условиями. Поставленная задача - "создать что-нибудь необычайное". 1 февраля 1991 года Патрик Нотон, Джеймс Гослинг и Майк Шеридан (Mike Sheridan) вплотную приступили к реализации проекта, который получил название Green.
    Цель они выбрали себе амбициозную - выяснить, какой будет следующая волна развития компьютерной индустрии (первыми считаются появление полупроводников и персональных компьютеров) и какие продукты необходимо разработать для успешного участия в ней. С самого начала проект не рассматривался как чисто исследовательский, задача была создать реальный продукт, устройство.
    На ежегодном собрании Sun весной 1991 года Гослинг заметил, что компьютерные чипы получили необычайное распространение, они применяются в видеомагнитофонах, тостерах, даже в дверных ручках гостиниц! Тем не менее, до сих пор в каждом доме можно увидеть до трех пультов дистанционного управления - для телевизора, видеомагнитофона и музыкального центра. Так родилась идея разработать небольшое устройство с жидкокристаллическим сенсорным экраном, которое будет взаимодействовать с пользователем с помощью анимации, показывая, чем можно управлять и как. Чтобы создать такой прибор, Нотон начал работать над специализированной графической системой, Гослинг взялся за программное обеспечение, а Шеридан занялся бизнес-вопросами.
    В апреле 1991 года команда покидает офис Sun, отключаясь даже от внутренней сети корпорации, и въезжает в новое помещение. Закупаются разнообразные бытовые электронные устройства, такие как игровые приставки Nintendo, телевизионные приставки, пульты дистанционного управления, и разработчики играют в различные игры целыми днями, чтобы лучше понять, как сделать пользовательский интерфейс легким в понимании и использовании. В качестве идеального примера Гослинг отмечал, что современные тостеры с микропроцессорами имеют точно такой же интерфейс , что и тостер его мамы, который служит уже 42 года. Очень быстро исследователи обнаружили, что практически все устройства построены на самых разных центральных процессорах. Это означает, что добавление новых функциональных возможностей крайне затруднено, так как необходимо учитывать ограничения и, как правило, довольно скудные возможности используемых чипов. Когда же Гослинг побывал на концерте, где смог воочию наблюдать сложное переплетение проводов, огромное количество колонок и полуавтоматических прожекторов, которые, казалось, согласованно двигаются в такт музыке, он понял, что будущее - за объединением сетей, компьютеров и других электронных устройств в единую согласованную инфраструктуру.
    Сначала Гослинг попытался модифицировать С++, чтобы создать язык для написания программ, минимально ориентированных на конкретные платформы. Однако очень скоро стало понятно, что это практически невозможно. Основное достоинство С++ - скорость программ, но отнюдь не их надежность. А надежность работы для обычных пользователей должна быть так же абсолютно гарантирована, как совместимость обычных электрических вилки и розетки. Поэтому в июне 1991 года Гослинг, который написал свой первый язык программирования в 14 лет, начинает разработку замены C++. Создавая новый каталог и раздумывая, как его назвать, он выглянул в окно, и взгляд его остановился на растущем под ним дереве. Так язык получил свое первое название - OaK (дуб). Спустя несколько лет, после проведения маркетинговых исследований, имя сменили на Java.
    Всего несколько месяцев потребовалось, чтобы довести разработку до стадии, когда стало возможным совместить новый язык с графической системой, над которой работал Нотон. Уже в августе команда смогла запустить первые программы, демонстрирующие возможности будущего устройства.
    Проект Green


    Само устройство, по замыслу создателей, должно было быть размером с обычный пульт дистанционного управления, работать от батареек, иметь привлекательный и забавный графический интерфейс и, в конце концов, стать любимой (и полезной!) домашней игрушкой. Чтобы построить этот не имеющий аналогов прибор, находчивые разработчики применили "технологию молотка". Они попросту находили какой-нибудь аппарат, в котором были подходящие детали или микросхемы, разбивали его молотком и таким образом добывали необходимые части. Так были получены основной жидкокристаллический экран, сенсорный экран и миниатюрные встроенные колонки. Центральный процессор и материнская плата были специально разработаны на основе высокопроизводительной рабочей станции Sun. Было придумано и оригинальное название - *7, или Star7 (с помощью этой комбинации кнопок можно было ответить с любого аппарата в офисе на звонок любого другого телефона, а поскольку редко кого из них можно было застать на рабочем месте, эти слова очень часто громко кричали на весь офис). Для придания интерфейсу большей привлекательности разработчики создали забавного персонажа по имени Дьюк (Duke), который всегда был готов помочь пользователю выполнить его задачу. В дальнейшем он стал спутником Java, счастливым талисманом - его можно встретить во многих документах, статьях, примерах кода.

    Задача была совершенно новая, не на что было опереться, не было никакого опыта, никаких предварительных наработок. Команда трудилась, не прерываясь ни на один день. В августе 1991 года состоялась первая демонстрация для Билла Джоя и Скотта МакНили. В ноябре группа снова подключилась к сети Sun по модемной линии. Чем дальше развивался проект, тем больше новых специалистов присоединялось к команде разработчиков. Примерно в то время было придумано название для той идеологии, которую они создавали,- 1st Person (условно можно перевести как "первое лицо").

    Наконец, 4 сентября 1992 года Star7 был завершен и продемонстрирован МакНили. Это было небольшое устройство с 5" цветным (16 бит) сенсорным экраном, без единой кнопки. Чтобы включить его, надо было просто дотронуться до экрана. Весь интерфейс был построен как мультик - никаких меню! Дьюк перемещался по комнатам нарисованного дома, а чтобы управлять им, надо было просто водить по экрану пальцем - никаких специальных средств управления. Можно было взять виртуальную телепрограмму с нарисованного дивана, выбрать передачу и "перетащить" ее на изображение видеомагнитофона, чтобы запрограммировать его на запись.

    Результат превзошел все ожидания! Стоит напомнить, что устройства типа карманных компьютеров (PDA), начиная с Newton, появились заметно позже, не говоря уже о цветном экране. Это было время 286i и 386i процессоров Intel (486i уже появились, но стоили очень дорого) и MS DOS, даже мышь еще не была обязательным атрибутом персонального компьютера.

    Руководители Sun были просто в восторге - появилось отличное оружие против таких могучих конкурентов, как HP, IBM и Microsoft. Новая технология была способна не только демонстрировать мультики. Объектно-ориентированный язык OaK обещал стать достаточно мощным инструментом для написания программ, которые могут работать в сетевом окружении. Его объекты, свободно распространяемые по сети, работали бы на любом устройстве, начиная с персонального компьютера и заканчивая обычными бытовыми видеомагнитофонами и тостерами. На презентациях Нотон представлял области применения OaK, изображая домашние компьютеры, машины, телефоны, телевизоры, банки и соединяя их единой сетью. Целое приложение, например, для работы с электронной почтой, могло быть построено в виде группы таких объектов, причем они необязательно должны были располагаться на одном устройстве. Более того, как язык, ориентированный на распределенную архитектуру, OaK имел механизмы безопасности, шифрования, процедур аутентификации, причем все эти возможности были встроенные, а значит, незаметные и удобные для пользователя.

    Сборка мусора

    Назад Вперед
    Одна из интереснейших особенностей языка программирования Java и среды выполнения приложений Java заключается в наличии специального процесса сборки мусора, предназначенного для удаления ненужных объектов из памяти. Эта система избавляет программиста от необходимости внимательно следить за использованием памяти, освобождая ненужные более области явным образом.
    Создавая объекты в Java, вы можете руководствоваться принципом "создай и забудь", так как система сборки мусора позаботится об удалении ваших объектов. Объект будет удален из памяти, как только на него не останется ни одной ссылки из других объектов.
    Приоритет процесса сборки мусора очень низкий, поэтому "уборка" среды выполнения приложений Java не отнимает ресурсы у самих приложений.
    Назад Вперед

    Сборка мусораСборка мусора Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Сборка мусора
    printmenus();
    Сборка мусораПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Сборка мусора»


    Сборка мусораSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Сборка мусора»


    Сборка мусораКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Сборка мусора»


    Сборка мусораГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Сборка мусора»


    Сборка мусораОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Сборка мусора»


    Сборка мусораПроекты
    События
    Lab Downloads
    Посмотреть все Сборка мусора»


    Сборка мусора

    Сетевые компьютеры

    Когда стало понятно, что новая технология пользуется небывалым спросом, разработчикам захотелось укрепить и развить успех и распространенность Java. Для того чтобы Java не разделила судьбу NeWS (эта оконная система упоминалась в начале лекции, она не получила развития, проиграв X Window), компания Sun старалась наладить сотрудничество с независимыми фирмами для производства различных библиотек, средств разработчика, инструментов. 9 января 1996 года было сформировано новое подразделение JavaSoft,которое и занялось разработкой новых Java-технологий и продвижением их на рынок. Главная цель - появление все большего количества самых разных приложений, написанных на этой платформе. Например, 1 июля 1997 года было объявлено, что ученые NASA (National Aeronautics and Space Administration, государственная организация США, занимающаяся исследованием космоса) с помощью Java-апплетов управляют роботом, изучающим поверхность Марса ("Java помогает делать историю!").
    Пора остановиться подробнее на том, почему по отношению к Java используется термин "платформа", чем Java отличается от обычного языка программирования.
    Как правило, платформой называют сочетание аппаратной архитектуры ("железо"), которая определяется типом используемого процессора (Intel x86, Sun SPARC, PowerPC и др.), с операционной системой (MS Windows, Sun Solaris, Linux, Mac OS и др.). При написании программ разработчик всегда пользуется средствами целевой платформы для доступа к сети, поддержки потоков исполнения, работы с графическим пользовательским интерфейсом (GUI) и другими возможностями. Конечно, различные платформы, в силу технических, исторических и других причин, поддерживают различные интерфейсы (API, Application Programming Interface), а значит, и программа может исполняться только под той платформой, под которую она была написана.
    Однако часто заказчикам требуется одна и та же функциональность, а платформы они используют разные. Задача портирования приложений стоит перед разработчиками давно. Редко удается перенести сложную программу без существенной переделки, очень часто различные платформы по-разному поддерживают многие возможности (например, операционная система Mac OS традиционно использует однокнопочную мышь, в то время как Windows изначально рассчитана на двухкнопочную).
    А значит, и языки программирования должны быть изначально ориентированы на какую-то конкретную платформу. Синтаксис и основные концепции легко распространить на любую систему (хотя это и не всегда эффективно), но библиотеки, компилятор и, естественно, бинарный исполняемый код специфичны для каждой платформы. Так было с самого начала эпохи компьютерных вычислений, а потому лишь немногие, действительно удачные программы поддерживались сразу на нескольких системах, что приводило к некоторой изоляции миров программного обеспечения для различных операционных систем.
    Было бы странно, если бы с развитием компьютерной индустрии разработчики не попытались создать универсальную платформу, под которой могли работать все программы. Особенно такому шагу способствовало бурное развитие Глобальной сети Internet, которая объединила пользователей независимо от типа используемых процессоров и операционных систем. Именно поэтому создатели Java задумали разработать не просто еще один язык программирования, а универсальную платформу для исполнения приложений, тем более что изначально OaK создавался для различных бытовых приборов, от которых ждать совместимости не приходится.
    Каким же образом можно "сгладить" различия и многообразие операционных систем? Способ не новый, но эффективный - с помощью виртуальной машины. Приложения на языке Java исполняются в специальной, универсальной среде, которая называется Java Virtual Machine. JVM - это программа, которая пишется специально для каждой реальной платформы, чтобы, с одной стороны, скрыть все ее особенности, а с другой - предоставить единую среду исполнения для Java -приложений. Фирма Sun и ее партнеры создали JVM практически для всех современных операционных систем. Когда речь идет о браузере с поддержкой Java , подразумевается, что в нем имеется встроенная виртуальная машина.
    Подробнее JVM рассматривается ниже, но необходимо сказать, что разработчики Sun приложили усилия, чтобы сделать эту машину вполне реальной, а не только виртуальной. 29 мая 1996 года объявляется операционная система Java OS (финальная версия выпущена в марте следующего года). Согласно пресс-релизу, это была "возможно, самая небольшая и быстрая операционная система, поддерживающая Java". Действительно, разработчики стремились к тому, чтобы обеспечить возможность исполнять Java -приложения на самом широком спектре устройств - сетевые компьютеры, карманные компьютеры (PDA), принтеры, игровые приставки, мобильные телефоны и т.д. Ожидалось, что Java OS будет реализована на всех аппаратных платформах. Это было необходимо для изначальной цели создателей Java - легкость добавления новой функциональности и совместимости в любые электрические приборы, которыми пользуется современный потребитель.
    Это был первый шаг, продвигающий платформу Java на один уровень вниз - на уровень операционных систем. Предполагалось сделать и следующий шаг - создать аппаратную архитектуру, центральный процессор, который бы напрямую выполнял инструкции Java безо всякой виртуальной машины. Устройство с такой реализацией стало бы полноценным Java-устройством.
    Кроме бытовых приборов, компания Sun позиционировала данное решение и для компьютерной индустрии - сетевые компьютеры должны были заменить разнородные платформы персональных рабочих станций. Такой подход хорошо укладывался в основную концепцию Sun, выраженную в лозунге "Сеть - это компьютер". Возможности одного компьютера никогда не сравнятся с возможностями сети, объединяющей все ресурсы компании, а тем более - всего мира. Наверное, сегодня это уже очевидно, но во времена, когда WWW еще не опутала планету, идея была революционной.
    Если же строить многофункциональную сеть, то к ее рабочим станциям предъявляются совсем другие требования - они не должны быть особенно мощными, вычислительные задачи можно переложить на серверы. Это даже более выгодно, так как позволяет централизовать поддержку и обновление программного обеспечения, а также не вынуждает сотрудников быть привязанными к своим рабочим местам. Достаточно войти с любого терминала в сеть, авторизоваться - и можно продолжать работу с того места, на котором она была оставлена. Это можно сделать в кабинете, зале для презентаций, кафе, в кресле самолета, дома - где угодно!
    Кроме очевидных удобств, это начинание было с большим энтузиазмом поддержано индустрией и в силу того, что оно являлось сильнейшим оружием в борьбе с крупнейшим производителем программного обеспечения - Microsoft. Тогда (да и сейчас) самой распространенной платформой являлась операционная система Windows на базе процессоров Intel (с чьей-то легкой руки теперь многими называемая Wintel). Этим компаниям удалось создать замкнутый круг, гарантирующий успех,- все пользовались их платформой, так как под нее написано больше всего программ, что, в свою очередь, заставляло разработчиков создавать новые продукты именно для платформы Wintel. Поскольку корпорация Microsoft всегда очень агрессивно развивала свое преимущество в области персональных компьютеров (вспомним, как Netscape Navigator безнадежно проиграл конкуренцию MS Internet Explorer), это не могло не вызывать сильное беспокойство других представителей компьютерной индустрии. Понятно, что концепция сетевых компьютеров свела бы на нет преимущества Wintel в случае широкого распространения. Разработчики просто перестали бы задумываться, что находится внутри их рабочей станции, также как домашние пользователи не имеют представления, на каких микросхемах собран их мобильный телефон или видеомагнитофон.
    Мы уже рассказывали о том, как и почему Microsoft лицензировала Java, хотя, казалось бы, этот шаг лишь способствовал опасному распространению новой технологии, ведь Internet Explorer завоевывал все большую популярность. Однако вскоре разразился судебный скандал. 30 сентября 1997 года вышел новый IE 4.0, а уже 7 октября Sun объявила, что этот продукт не проходит тесты на соответствие со спецификацией виртуальной машины. 18 ноября Sun обращается в суд, чтобы запретить использование логотипа "Совместимый с Java" ("Java compatible") для MS IE 4.0. Оказалось, что разработчики Microsoft слегка "улучшили" язык Java , добавив несколько новых ключевых слов и библиотек. Не то что бы это были сверхмощные расширения, однако достаточно привлекательные для того, чтобы значительная часть разработчиков начала ее использовать. К счастью, в Sun быстро осознали всю степень опасности такого шага. Java могла потерять звание универсальной платформы, для которой верен знаменитый девиз "Write once, run everywhere" ("Написано однажды, работает везде"). В таком случае она утратила бы основу своего успеха, превратившись всего лишь в "еще один язык программирования".
    Компании Sun удалось отстоять свою технологию. 24 марта 1998 года суд согласился с требованиями компании (конечно, это было только предварительное решение, дело завершилось лишь 23 января 2001 года - Sun получил компенсацию в 20 миллионов долларов и добился выполнения лицензионного соглашения), а уже 12 мая Sun снова выступает с требованием обязать Microsoft включить полноценную версию Java в Windows 98 и другие программные продукты. Эта тяжба продолжается до сих пор с переменным успехом сторон. Например, Microsoft исключила из виртуальной машины Internet Explorer библиотеку java.rmi, позволяющую создавать распределенные приложения, пытаясь привлечь внимание разработчиков к DCOM-технологии, жестко привязанной к платформе Win32. В ответ многие компании стали распространять специальное дополнение (patch), устраняющее этот недостаток. В результате Microsoft остановила свою поддержку Java на версии 1.1, которая на данный момент является устаревшей и не имеет многих полезных возможностей. Это, в свою очередь, практически остановило широкое распространение апплетов, кроме случаев либо совсем несложной функциональности (типа бегущей строки или диалога с несколькими полями ввода и кнопками), либо приложений для внутренних сетей корпораций. Для последнего случая Sun выпустил специальный продукт Java Plug-in , который встраивается в MS IE и NN, позволяя им исполнять апплеты на основе Java самых последних версий, причем полное соответствие спецификациям гарантируется (первоначально продукт назывался Java Activator и впервые был объявлен 10 декабря 1997 года). На данный момент Microsoft то включает, то исключает Java из своей операционной системы Windows XP, видимо, пытаясь найти самый выгодный для себя вариант.
    Что же касается сетевых компьютеров и Java OS, то, увы, они пока не нашли своих потребителей. Видимо, обычные персональные рабочие станции в совокупности с JVM требуют гораздо меньше технологических и маркетинговых усилий и при этом вполне успешно справляются с прикладными задачами. А Java, в свою очередь, стала позиционироваться для создания сложных серверных приложений.

    Сложности внутри Sun Microsystems

    Действительно, события начинают разворачиваться в декабре 1990 года, когда бурное развитие WWW (World Wide Web - "всемирная паутина") никто не мог еще даже предсказать. Тогда компьютерная индустрия была поглощена взлетом персональных компьютеров. К сожалению, фирма Sun Microsystems, занимающая значительную долю рынка серверов и высокопроизводительных станций, по мнению многих сотрудников и независимых экспертов, не могла предложить ничего интересного для обычных пользователей "персоналок" - для них компьютеры от Sun представлялись "слишком сложными, очень некрасивыми и чересчур "тупыми" устройствами".
    Поэтому Скотт МакНили (Scott McNealy), член совета директоров, президент и CEO (исполнительный директор) корпорации Sun, не был удивлен, когда 25-летний хорошо зарекомендовавший себя программист Патрик Нотон (Patrick Naughton), проработав всего 3 года, объявил о своем желании перейти в компанию NeXT. Они были друзьями, и Патрик объяснил свое решение просто и коротко: "Они все делают правильно". Скотт задумался на секунду и произнес историческую фразу. Он попросил Патрика перед уходом описать, что, по его мнению, в Sun делается неверно. Надо было не просто рассказать о проблеме, но предложить решение, не оглядываясь на существующие правила и традиции, как будто в его распоряжении имеются неограниченные ресурсы и возможности.
    Патрик Нотон выполнил просьбу. Он безжалостно раскритиковал новую программную архитектуру NeWS, над которой фирма работала в то время, а также высоко оценил только что объявленную операционную систему NeXTstep. Нотон предложил привлечь профессиональных художников-дизайнеров, чтобы сделать пользовательские интерфейсы Sun более привлекательными; выбрать одно средство разработки и сконцентрировать усилия на одной оконной технологии, а не на нескольких сразу (Нотон был вынужден поддерживать сотни различных комбинаций технологий, платформ и интерфейсов , используемых в компании); наконец, уволить почти всех сотрудников из Window Systems Group (если выполнить предыдущие условия, они будут просто не нужны).
    Конечно, Нотон был уверен, что его письмо просто проигнорируют, но все же отложил свой переход в NeXT в ожидании какой-нибудь ответной реакции. Однако она превзошла все ожидания.
    МакНили разослал письмо Нотона всему управляющему составу корпорации, а те переслали его своим ведущим специалистам. Откликнулись буквально все, и, по общему мнению, Нотон описал то, о чем все думали, но боялись высказать. Решающей оказалась поддержка Билла Джоя (Bill Joy) и Джеймса Гослинга (James Gosling). Билл Джой - один из основателей и вице-президент Sun, а также участник проекта по созданию операционной системы UNIX в университете Беркли. Джеймс Гослинг пришел в Sun в 1984 году (до этого он работал в исследовательской лаборатории IBM) и был ведущим разработчиком, а также автором первой реализации текстового редактора EMACS на C. Эти люди имели огромный авторитет в корпорации.
    Чтобы не останавливаться на достигнутом, Нотон решил предложить какой-то совершенно новый проект. Он объединился с группой технических специалистов, и они просидели до 4.30 утра, обсуждая базовые концепции такого проекта. Их получилось всего три: главное - потребитель, и все строится исключительно в соответствии с его интересами; небольшая команда должна спроектировать небольшую же аппаратно-программную платформу; эту платформу нужно воплотить в устройстве, предназначенном для персонального пользования, удобном и простом в обращении - т.е. создать компьютер для обычных людей. Этих идей оказалось достаточно, чтобы Джон Гейдж (John Gage), руководитель научных исследований Sun, смог организовать презентацию для высшего руководства корпорации. Нотон изложил условия, которые он считал необходимыми для успешного развития этого предприятия: команда должна расположиться вне офиса Sun, чтобы не испытывать никакого сопротивления революционным идеям; проект будет секретным для всех, кроме высшего руководства Sun; аппаратная и программная платформы могут быть несовместимы с продуктами Sun; на первый год группе необходим миллион долларов.

    Ссылки на методы класса

    Так как в Java нет указателей, нет возможности ссылаться на методы с помощью оператора ->. Для ссылки на метод класса используется только оператор "точка".
    Оператор "::" также не определен в Java. Если вам необходимо вызвать метод из базового класса, следует использовать ключевое слово super.

    Указатели, которых нет

    Назад Вперед
    Самая большая и шокирующая новость для тех, кто раньше программировал на С, а теперь занялся изучением Java, это то, что в языке Java нет указателей. Традиционно считалось, что работать с указателями трудно, а их использование приводит к появлению трудно обнаруживаемых ошибок. Поэтому разработчики Java решили отказаться от использования указателей совсем.
    Спешим успокоить - вы сможете успешно составлять приложения Java и без указателей, несмотря на то что вам, возможно, придется немного изменить стиль программирования.
    Вы можете спросить: как же передавать функциям ссылки на объекты, если нет указателей?
    Если вам нужно передать ссылку на переменную базового типа, такого, например, как int или long, то ничего не получится - мы уже говорили, что переменные базовых типов передаются по значению, а не по ссылке. Поэтому нельзя напрямую создать на языке Java эквивалент следующей программы, составленной на языке С:
    // Некоторая переменная int nSomeValue;
    // Функция, изменяющая значение переменной, // заданной своим адресом void StoreValue(int *pVar, int nNewValue) { pVar->nNewValue; } . . . StoreValue(&nSomeValue, 10);
    Выход, однако, есть.
    Язык Java позволяет использовать вместо указателей ссылки на объекты. Пользуясь этими ссылками, вы можете адресовать объекты по их имени, вызывая методы и изменяя значения данных объектов.
    Что же касается данных базовых типов, если вам нужно передавать на них ссылки, то следует заменить базовые типы на соответствующие замещающие классы. Например, вместо типа int используйте класс Integer, вместо типа long - класс Long и так далее.
    Инициализация таких объектов должна выполняться с помощью конструктора, как это показано ниже:
    Integer nSomeValue; nSomeValue = new Integer(10);
    Первая строка создает неинициализированную ссылку с именем nSomeValue и типом Integer. При попытке использования такой ссылки возникнет исключение.
    Вторая строка создает объект класса Integer, вызывая конструктор. Этот конструктор определяет начальное значение. После выполнения оператора присваивания ссылка nSomeValue будет ссылаться на реальный объект класса Integer и ее можно будет использовать.

    Возрождение OaK

    Для победного выхода OaK не хватало последнего штриха - браузера, который поддерживал бы эту технологию. Именно он должен был стать тем самым "убойным" приложением Нотона, которое завершало почти пятилетнюю подготовительную работу перед официальным объявлением новой платформы.
    Браузер назвали WebRunner. Нотону потребовался всего один выходной, чтобы написать основную часть программы. Это было в июле, а в сентябре 1994 года WebRunner уже демонстрировался руководству Sun. Небольшие программы, написанные на OaK для распространения через Internet, назвали апплетами (applets).
    Возрождение OaK

    Следующая демонстрация происходила на конференции, где встречались разработчики Internet-приложений и представители индустрии развлечений. Когда Гослинг начал презентацию WebRunner, слушатели не проявили большого интереса, решив, что это просто клон Mosaic. Тогда Гослинг провел мышкой над сложной трехмерной моделью химической молекулы.
    Следуя за курсором, модель поворачивалась по всем направлениям! Сейчас данная функция, возможно, не производит такого впечатления, однако в то время это было подобно переходу от картинки к кинематографу. Следующий пример демонстрировал анимированную сортировку. Вначале изображался набор отрезков разной длины. Затем синяя и красная линии начинали бегать по этому набору, сортируя отрезки по размеру. Пример тоже нехитрый, однако наглядно демонстрирующий, что на стороне клиента появилась полноценная программная платформа. Оба эти апплета сейчас являются стандартными примерами и входят в состав Java Development Kit любой версии. Успех демонстрации, которая закончилась бурными аплодисментами, показал, что OaK и WebRunner способны устроить революцию в Internet, так как все участники конференции по-другому взглянули на возможности, которые предоставляет Всемирная Сеть.
    Кстати, в начале 1995 года, когда стало ясно, что официальное объявление уже не за горами, за дело взялись маркетологи. В результате их исследований OaK был переименован в Java, а WebRunner стал называться HotJava. Многие тогда недоумевали, что же послужило поводом для такого решения. Легенда гласит, что Java - это сорт кофе (такой кофе действительно есть), который очень любили программисты. Видимо, похожим образом родилось и название HotJava ("горячая Java"). Тема кофе навсегда останется в названиях и логотипах (технология создания компонентов названа Java Beans - зерна кофе, специальный формат для архивирования файлов с Java-программами JAR - банка с кофе и т.д.), а сам язык критики стали называть "для кофеварок". Впрочем, сейчас все уже привыкли и не задумываются над названием, возможно, на это и было рассчитано (а тем, кто продолжает выражать недовольство, приводят альтернативные варианты, которые рассматривались тогда - Neon, Lyric, Pepper или Silk).
    Согласно плану, спецификация Java, реализация платформы и HotJava должны были свободно распространяться через Internet. С одной стороны, это позволяло в кратчайшие сроки распространить технологию по всему миру и сделать ее стандартом де-факто для Internet-программирования. С другой стороны, при участии всего сообщества разработчиков, которые высказывали бы свои замечания, можно было гораздо быстрее устранить все возможные ошибки и недоработки. Однако в конце 1994 года лишь считанные копии были распространены за пределы Sun. В феврале 1995 года выходит, возможно, первый пресс-релиз, сообщающий, что вскоре будут доступны альфа-версии OaK и WebRunner.
    Когда это произошло, команда стала подсчитывать случаи загрузки их продукта для просмотра. Вскоре пришлось считать уже сотнями. Затем решили, что если удастся достигнуть 10.000, то это будет просто ошеломляющий успех. Ждать пришлось совсем не так долго, как можно было предположить. Интерес нарастал лавинообразно, после просмотров приходило большое количество писем и мощности Internet-канала стало не хватать. На письма всегда отвечали очень подробно, что поначалу можно было делать, не отрываясь от работы. Затем по очереди стали назначать одного разработчика, чтобы он в течение недели только писал ответы. Наконец, потребовался специальный сотрудник, так как приходило уже по 2-3 тысячи писем в день. Вскоре руководство Sun осознало, что такой мощный успех Java не имеет никакого бюджета или плана для рекламы и других акций продвижения на рынок. Первым шагом в этом направлении становится публикация 23 марта 1995 года в газете Sun Jose Mercury News статьи с описанием новой технологии, где был приведен адрес официального сайта http://java.sun.com/, который и по сей день является основным источником информации по Java.

    Встроенные классы

    В языке Java все классы происходят от класса Object, и, соответственно, наследуют методы этого класса. Некоторые библиотеки классов подключаются автоматически, и мы будем называть их встроенными. К таким относится, в частности, библиотека с названием java.lang. Другие библиотеки классов вы должны подключать в исходном тексте приложения Java явным образом с помощью оператора import.

    World Wide Web

    В погоне за призраком интерактивного телевидения многие участники компьютерного рынка пропустили поистине эпохальное событие. В апреле 1993 года Марк Андриссен (Marc Andreessen) и Эрик Бина (Eric Bina), работающие в Национальном центре суперкомпьютерных приложений (National Center for Supercomputing Applications, NCSA) при университете Иллинойс, выпустили первую версию графического браузера ("обозревателя") Mosaic 1.0 для WWW. Хотя Internet существовал на тот момент уже около 20 лет, имеющимися протоколами связи (FTP, telnet и др.) пользоваться было очень неудобно и Глобальная Сеть использовалась лишь в академической и государственной среде. Mosaic же основывался на новом языке разметки гипертекстовых документов (HyperText Markup Language, HTML), который с 1991 года разрабатывался в Европейском институте физики частиц (CERN) специально для представления информации в Internet. Этот формат позволял просматривать текст и изображения, а главное - поддерживал ссылки, с помощью которых можно было одним нажатием мыши перейти как на другую часть той же страницы, так и на страницу, которая могла располагаться совсем в другой части сети и в любой точке планеты. Именно такие перекрестные обращения, используя которые, пользователь мог незаметно для себя посетить множество узлов Internet, и позволили считать все HTML-документы связанными частями единого целого - Всемирной Паутины (World Wide Web, WWW).
    И самое важное - все эти новые достижения были совершенно бесплатны и доступны для всех желающих. Впервые обычные пользователи персональных компьютеров безо всякой специальной подготовки могли пользоваться глобальной сетью не только для решения рабочих вопросов, но и для поиска информации на самые разные темы. Количество документов в пространстве WWW стало расти экспоненциально, и очень скоро сеть Internet стала поистине Всемирной. Правда, со временем обнаружилось, что такой способ организации и хранения информации очень напоминает свалку, в которой крайне трудно найти данные по какому-нибудь конкретному вопросу, однако эта тема относится к совершенно другому этапу развития компьютерного мира. Итак, каким-то непостижимым образом Sun не замечает зарождения новой эпохи. Технический директор Sun впервые увидел Mosaic лишь три месяца спустя! И это притом, что около 50% серверов и рабочих станций в сети Internet были произведены именно Sun.
    Новый бизнес-план FirstPerson ставил цель, которая была неким промежуточным шагом от интерактивного телевидения к возможностям Internet. Идея заключалась в создании платформы для кабельных компаний, пользователями которой были бы обычные владельцы персональных компьютеров, объединенные сетями таких компаний. Используя технологию OaK, разработчики могли бы создавать приложения, по функциональности аналогичные программам, распространяемым на CD-ROM, однако обладающие интерактивностью, позволяющей людям обмениваться любой информацией через сеть. Ожидалось, что такие сети в итоге и разовьются в интерактивное телевидение, и тогда OaK станет полноценным решением для этой индустрии. Об Internet и Mosaic пока не говорилось ни слова.
    По многим причинам этот план не устроил руководство Sun (он не вполне соответствовал главному ожиданию - новая разработка должна была привести к увеличению спроса на продукты Sun). Из-за отсутствия перспектив половина сотрудников FirstPerson была переведена в только что созданную команду Sun Interactive, которая продолжила заниматься мультимедиа-сервисами уже без OaK. Все предприятие оказалось под угрозой бесславной кончины, однако в этот момент Билл Джой снова оказал поддержку проекту, который вскоре дал миру платформу Java.
    Когда создатели FirstPerson, наконец, обратили внимание на Internet, они поняли, что функциональность тех сетевых приложений, для которых создавался OaK, очень близка к WWW. Билл Джой вспомнил, как он двадцать лет назад принимал участие в разработке UNIX в Беркли и затем эта операционная система получила широчайшее распространение благодаря тому, что ее можно было загрузить по сети бесплатно. Такой принцип бесплатного распространения коммерческих продуктов создал саму WWW, тем же путем компания Netscape вскоре стала лидером рынка браузеров, так многие технологии получили возможность захватить долю рынка в кратчайшие сроки. Эти новые идеи при поддержке Джоя окончательно убедили руководство Sun, что Internet поможет воскресить платформу OaK (кстати, этот новый проект поначалу называли "Liveoak"). В итоге Джой садится писать очередной бизнес-план и отправляет Гослинга и Нотона начинать работу по адаптации OaK для Internet. Гослинг пересматривает программный код платформы, а Нотон берется за написание "убойного" приложения, которое сразу бы продемонстрировало всю мощь OaK для Internet.
    В самом деле, эти технологии прекрасно подошли друг другу. Языки программирования всегда играли важную роль в развитии компьютерных технологий. Мэйнфреймы не были особенно полезны, пока не появился Cobol. Благодаря языку Fortran от IBM, компьютеры стали широко применяться для научных вычислений и исследований. Basic - самый первый продукт от Microsoft - позволил всем программистам-любителям создавать программы для своих персональных компьютеров. Язык С++ стал основой для развития графических пользовательских интерфейсов , таких как Mac OS и Windows. Создатели OaK сделали все, чтобы эта технология сыграла такую же роль в программировании для Internet.
    Несмотря на то, что к середине 1994 года WWW достиг невиданных размеров (конечно, по меркам того времени), web-страницы по-прежнему были скорее похожи на обычные бумажные издания, чем на интерактивные приложения. По большей части вся работа в сети заключалась в отправке запроса на web-сервер и получении ответа, который содержал обычный статический HTML-файл, отображаемый браузером на стороне клиента. Уже тогда функциональность web-серверов расширялась с помощью CGI (Common Gateway Interface). Эта технология позволяла по запросу клиента запускать на сервере обычную программу и ее результат отсылать обратно в качестве ответа. Поскольку в то время скорость каналов связи была невысокой (хотя, похоже, пользователи никогда не будут удовлетворены возможностями аппаратуры), клиент мог ждать несколько минут, чтобы лишь увидеть сообщение о том, что он ошибся в одной букве запроса. Динамическое построение графиков при таком способе реализации означало бы генерацию GIF-файлов в реальном времени. А ведь зачастую клиентские машины являются полноценными персональными компьютерами, которые могли бы брать значительную часть работы взаимодействия с пользователем на себя, разгружая серверы.
    Вообще, клиент-серверная архитектура, просто необходимая для большинства сложных корпоративных (enterprise) приложений, обладает рядом существенных технических сложностей. Основная идея - разместить общие данные на сервере, чтобы создать единое информационное пространство для работы многих пользователей, а программы, отображающие и позволяющие удобно редактировать эти данные, выполняются на клиентских машинах. Очень часто в корпорации используется несколько аппаратных платформ (это может быть как "историческое наследие", так и следствие того, что различные подразделения, решая свои задачи, нуждаются в различных компьютерах). Следовательно, приложение необходимо развивать сразу в нескольких вариантах, что существенно увеличивает стоимость поддержки. Кроме того, обновление клиентской части означает, что нужно перенастроить все компьютеры компании в кратчайший срок. А ведь обновлениями часто занимаются несколько групп разработчиков.
    Попытка придать Internet-браузерам возможности полноценного клиентского приложения встречает еще большие трудности. Во-первых, обычные сложности предельно возрастают - в Internet представлены практически все существующие платформы, а количество и географическая распределенность пользователей делает быстрое обновление просто невозможным. Во-вторых, особенно остро встает вопрос безопасности. Через сеть удивительно быстро распространяется не только важная информация, но и вирусы. Текстовая информация и изображения не несут в себе никакой угрозы для клиентской машины, другое дело - исполняемый код. Наконец, приложения с красивым и удобным графическим интерфейсом, как правило, имели немаленький размер, недаром основным средством их распространения были CD-ROM'ы. Понятно, что для Internet необходимо было серьезно поработать над компактностью кода.
    Если оглянуться на историю развития OaK, становится понятно, что эта платформа удивительным образом отвечает всем перечисленным требованиям Internet-программирования, хотя и создавалась во времена, когда про WWW никто даже и не думал. Видимо, это говорит о том, насколько верно предугадали развитие индустрии участники проекта Green.

    В этой лекции мы рассказали

    В этой лекции мы рассказали о том, какая непростая ситуация сложилась в корпорации Sun в эпоху развития персональных компьютеров в конце 1990 года. Патрик Нотон в своем письме сумел выявить истинные причины такого положения и обозначить истинные цели для создания успешного продукта. Благодаря этому при поддержке Джеймса Гослинга начался проект Green. Одним из продуктов, созданных в рамках этого проекта, стала совершенно новая платформа OaK. Для ее продвижения Sun учредила дочернюю компанию FirstPerson, но настоящий успех пришел, когда платформу, переименовав в Java, сориентировали на применение в Internet.
    Глобальная сеть появилась в апреле 1993 года с выходом первого браузера Mosaic 1.0 и завоевывала пользовательскую аудиторию с поразительной скоростью. Первым примером Java-приложений стали апплеты, запускаемые при помощи специально созданного браузера HotJava. Наконец, после почти четырехлетней истории создания и развития, Java была официально представлена миру. Благодаря подписанию лицензионного соглашения с Netscape, это событие стало поистине триумфальным.
    Были рассмотрены различные варианты применения Java . Отдельно был описан язык Java Script, который, несмотря на сходство в названии, имеет не так много общего с Java . Подробно рассмотрены отличительные особенности Java . Описаны базовые продукты от Sun: JDK и JRE. Кратко освещена история развития версий платформы Java, включая добавляемые технологии и продукты.

    Замещающие классы

    Очень часто в наших приложениях вместо базовых типов переменных мы будем использовать объекты встроенных классов, которые называются замещающими классами (wrapper classes). Ниже мы перечислили названия этих классов и названия базовых типов данных, которые они замещают:

    Базовый тип данных Замещающий класс
    boolean Boolean
    char Character
    int Integer
    long Long
    float Float
    double Double

    Заметим, что для преобразования базовых типов данных в объекты замещающего класса и обратно вы не можете применять оператор присваивания. Вместо этого необходимо использовать соответствующие конструкторы и методы замещающих классов.

    Программирование на Java

    Агрегация

    Отношение между классами типа "содержит" (contain) или "состоит из" называется агрегацией, или включением. Например, если аквариум наполнен водой и в нем плавают рыбки, то можно сказать, что аквариум агрегирует в себе воду и рыбок.
    Агрегация

    Такое отношение включения, или агрегации (aggregation), изображается линией с ромбиком на стороне того класса, который выступает в качестве владельца, или контейнера. Необязательное название отношения записывается посередине линии.
    В нашем примере отношение contain является двунаправленным. Объект класса Aquarium содержит несколько объектов Fish. В то же время каждая рыбка "знает", в каком именно аквариуме она живет. Каждый класс имеет свою роль в агрегации, которая указывает, какое место занимает класс в данном отношении. Имя роли не является обязательным элементом обозначений и может отсутствовать на диаграмме. В примере можно видеть роль home класса Aquarium (аквариум является домом для рыбок), а также роль inhabitants класса Fish (рыбки являются обитателями аквариума). Название роли обычно совпадает с названием соответствующего поля в классе. Изображение такого поля на диаграмме излишне, если уже указано имя роли. Т.е. в данном случае класс Aquarium будет иметь свойство (поле) inhabitants, а класс Fish - свойство home.
    Число объектов, участвующих в отношении, записывается рядом с именем роли. Запись "0..n" означает "от нуля до бесконечности". Приняты также обозначения:
  • "1..n" - от единицы до бесконечности;
  • "0" - ноль;
  • "1" - один;
  • "n" - фиксированное количество;
  • "0..1" - ноль или один.

  • Код, описывающий рассмотренную модель и явление агрегации, может выглядеть, например, следующим образом:
    // определение класса Fish public class Fish { // определения поля home // (ссылка на объект Aquarium) private Aquarium home;
    public Fish() { } } // определение класса Aquarium public class Aquarium { // определения поля inhabitants // (массив ссылок на объекты Fish) private Fish inhabitants[]; public Aquarium() { } }

    Ассоциация

    Если объекты одного класса ссылаются на один или более объектов другого класса, но ни в ту, ни в другую сторону отношение между объектами не носит характера "владения", или контейнеризации, такое отношение называют ассоциацией (association). Отношение ассоциации изображается так же, как и отношение агрегации, но линия, связывающая классы,- простая, без ромбика.
    В качестве примера можно рассмотреть программиста и его компьютер. Между этими двумя объектами нет агрегации, но существует четкая взаимосвязь. Так, всегда можно установить, за какими компьютерами работает какой-либо программист, а также какие люди пользуются отдельно взятым компьютером. В рассмотренном примере имеет место ассоциация "многие-ко-многим".
    Ассоциация

    В данном случае между экземплярами классов Programmer и Computer в обе стороны используется отношение "0..n", т.к. программист, в принципе, может не работать с компьютером (если он теоретик или на пенсии). В свою очередь, компьютер может никем не использоваться (если он новый и еще не установлен).
    Код, соответствующий рассмотренному примеру, будет, например, следующим:
    public class Programmer { private Computer computers[]; public Programmer() { } } public class Computer { private Programmer programmers[]; public Computer() { } }

    Достоинства ООП

    От любой методики разработки программного обеспечения мы ждем, что она поможет нам в решении наших задач. Но одной из самых значительных проблем проектирования является сложность. Чем больше и сложнее программная система, тем важнее разбить ее на небольшие, четко очерченные части. Чтобы справиться со сложностью, необходимо абстрагироваться от деталей. В этом смысле классы представляют собой весьма удобный инструмент.

  • Классы позволяют проводить конструирование из полезных компонентов, обладающих простыми инструментами, что позволяет абстрагироваться от деталей реализации.
  • Данные и операции над ними образуют определенную сущность, и они не разносятся по всей программе, как нередко бывает в случае процедурного программирования, а описываются вместе. Локализация кода и данных улучшает наглядность и удобство сопровождения программного обеспечения.

  • Инкапсуляция позволяет привнести свойство модульности, что облегчает распараллеливание выполнения задачи между несколькими исполнителями и обновление версий отдельных компонентов.

  • ООП дает возможность создавать расширяемые системы. Это одно из основных достоинств ООП, и именно оно отличает данный подход от традиционных методов программирования. Расширяемость означает, что существующую систему можно заставить работать с новыми компонентами, причем без внесения в нее каких-либо изменений. Компоненты могут быть добавлены на этапе исполнения программы.
    Полиморфизм оказывается полезным преимущественно в следующих ситуациях.
  • Обработка разнородных структур данных. Программы могут работать, не различая вида объектов, что существенно упрощает код. Новые виды могут быть добавлены в любой момент.
  • Изменение поведения во время исполнения. На этапе исполнения один объект может быть заменен другим, что позволяет легко, без изменения кода, адаптировать алгоритм в зависимости от того, какой используется объект.
  • Реализация работы с наследниками. Алгоритмы можно обобщить настолько, что они уже смогут работать более чем с одним видом объектов.
  • Создание "каркаса" (framework). Независимые от приложения части предметной области могут быть реализованы в виде набора универсальных классов, или каркаса (framework), и в дальнейшем расширены за счет добавления частей, специфичных для конкретного приложения.

  • Часто многоразового использования программного обеспечения не удается добиться из-за того, что существующие компоненты уже не отвечают новым требованиям. ООП помогает этого достичь без нарушения работы уже имеющихся клиентов, что позволяет извлечь максимум из многоразового использования компонентов.
  • Сокращается время на разработку, которое может быть отдано другим задачам.
  • Компоненты многоразового использования обычно содержат гораздо меньше ошибок, чем вновь разработанные, ведь они уже не раз подвергались проверке.
  • Когда некий компонент используется сразу несколькими клиентами, улучшения, вносимые в его код, одновременно оказывают положительное влияние и на множество работающих с ним программ.
  • Если программа опирается на стандартные компоненты, ее структура и пользовательский интерфейс становятся более унифицированными, что облегчает ее понимание и упрощает использование.


  • Инкапсуляция

    Инкапсуляция (encapsulation) - это сокрытие реализации класса и отделение его внутреннего представления от внешнего (интерфейса). При использовании объектно-ориентированного подхода не принято применять прямой доступ к свойствам какого-либо класса из методов других классов. Для доступа к свойствам класса принято задействовать специальные методы этого класса для получения и изменения его свойств.
    Внутри объекта данные и методы могут обладать различной степенью открытости (или доступности). Степени доступности, принятые в языке Java, подробно будут рассмотрены в лекции 6. Они позволяют более тонко управлять свойством инкапсуляции.
    Открытые члены класса составляют внешний интерфейс объекта. Это та функциональность, которая доступна другим классам. Закрытыми обычно объявляются все свойства класса, а также вспомогательные методы, которые являются деталями реализации и от которых не должны зависеть другие части системы.
    Благодаря сокрытию реализации за внешним интерфейсом класса можно менять внутреннюю логику отдельного класса, не меняя код остальных компонентов системы. Это свойство называется модульность.
    Обеспечение доступа к свойствам класса только через его методы также дает ряд преимуществ. Во-первых, так гораздо проще контролировать корректные значения полей, ведь прямое обращение к свойствам отслеживать невозможно, а значит, им могут присвоить некорректные значения.
    Во-вторых, не составит труда изменить способ хранения данных. Если информация станет храниться не в памяти, а в долговременном хранилище, таком как файловая система или база данных, потребуется изменить лишь ряд методов одного класса, а не вводить эту функциональность во все части системы.
    Наконец, программный код, написанный с использованием данного принципа, легче отлаживать. Для того, чтобы узнать, кто и когда изменил свойство интересующего нас объекта, достаточно добавить вывод отладочной информации в тот метод объекта, посредством которого осуществляется доступ к свойству этого объекта. При использовании прямого доступа к свойствам объектов программисту пришлось бы добавлять вывод отладочной информации во все участки кода, где используется интересующий нас объект.

    Исходный текст приложения HelloJava

    Назад Вперед
    Исходный текст нашего первого приложения состоит всего из нескольких строк:
    public class HelloJava { public static void main(String args[]) { System.out.println("Hello, Java!"); } }
    По своей простоте он не уступает известной программе "Hello, world!", с которой обычно начинают изучение языка программирования C.
    В нашем приложении определен один класс типа public с именем HelloJava. Заметим, что исходный файл приложения Java может содержать только один класс public, причем имя файла должно в точности совпадать с именем такого класса. В данном случае исходный файл называется HelloJava.java. Если бы вы назвали файл helloJava.java, компилятор выдал бы сообщение об ошибке.
    В классе HelloJava мастером проектов автоматически создается один статический метод с именем main.
    Если класс типа public с именем, совпадающем с именем файла, содержит определение метода main, то такой метод служит точкой входа автономного приложения Java. В этом он напоминает функцию main обычной программы, составленной на языке программирования C.
    В качестве параметра методу main передается ссылка на массив строк класса String. Через эти строки вы можете передавать приложению Java параметры запуска.
    Как наше приложение выводит текстовую строку на консоль?
    В классе System определена переменная класса PrintStream с именем out. В классе PrintStream определен метод println, при помощи которого приложение HelloJava выводит сообщение "Hello, Java!" на консоль.
    Но где же объект, для которого вызывается метод println? В классе System поле PrintStream определено как статическое, поэтому методы этого класса можно вызывать, не создавая объектов класса System.
    Назад Вперед

    Исходный текст приложения HelloJavaИсходный текст приложения HelloJava Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Исходный текст приложения HelloJava
    printmenus();
    Исходный текст приложения HelloJavaПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Исходный текст приложения HelloJava»


    Исходный текст приложения HelloJavaSolaris 10

    Классы

    Все монеты из предыдущего примера принадлежат одному и тому же классу объектов (именно с этим связана их одинаковость). Номинальная стоимость монеты, металл, из которого она изготовлена, форма - это атрибуты класса. Совокупность атрибутов и их значений характеризует объект. Наряду с термином "атрибут" часто используют термины "свойство" и "поле", которые в объектно-ориентированном программировании являются синонимами.
    Все объекты одного и того же класса описываются одинаковыми наборами атрибутов. Однако объединение объектов в классы определяется не наборами атрибутов, а семантикой. Так, например, объекты "конюшня" и "лошадь" могут иметь одинаковые атрибуты: цена и возраст. При этом они могут относиться к одному классу, если рассматриваются в задаче просто как товар, либо к разным классам, если в рамках поставленной задачи будут использоваться по-разному, т.е. над ними будут совершаться различные действия.
    Объединение объектов в классы позволяет рассмотреть задачу в более общей постановке. Класс имеет имя (например, "лошадь"), которое относится ко всем объектам этого класса. Кроме того, в классе вводятся имена атрибутов, которые определены для объектов. В этом смысле описание класса аналогично описанию типа структуры или записи (record), широко применяющихся в процедурном программировании; при этом каждый объект имеет тот же смысл, что и экземпляр структуры (переменная или константа соответствующего типа).
    Формально класс - это шаблон поведения объектов определенного типа с заданными параметрами, определяющими состояние. Все экземпляры одного класса (объекты, порожденные от одного класса) имеют один и тот же набор свойств и общее поведение, то есть одинаково реагируют на одинаковые сообщения.
    Классы

    В соответствии с UML (Unified Modelling Language - унифицированный язык моделирования), класс имеет следующее графическое представление.
    Класс изображается в виде прямоугольника, состоящего из трех частей. В верхней части помещается название класса, в средней - свойства объектов класса, в нижней - действия, которые можно выполнять с объектами данного класса (методы).
    Каждый класс также может иметь специальные методы, которые автоматически вызываются при создании и уничтожении объектов этого класса:

  • конструктор (constructor) - выполняется при создании объектов;

  • деструктор (destructor) - выполняется при уничтожении объектов.

  • Обычно конструктор и деструктор имеют специальный синтаксис, который может отличаться от синтаксиса, используемого для написания обычных методов класса.

    Метаклассы

    Итак, любой объект имеет структуру, состоящую из полей и методов. Объекты, имеющие одинаковую структуру и семантику, описываются одним классом, который и является, по сути, определением структуры объектов, порожденных от него.
    В свою очередь, каждый класс, или описание, всегда имеет строгий шаблон, задаваемый языком программирования или выбранной объектной моделью. Он определяет, например, допустимо ли множественное наследование, какие существуют ограничения на именование классов, как описываются поля и методы, набор существующих типов данных и многое другое. Таким образом, класс можно рассматривать как объект, у которого есть свойства (имя, список полей и их типы, список методов, список аргументов для каждого метода и т.д.). Также класс может обладать поведением, то есть поддерживать методы. А раз для любого объекта существует шаблон, описывающий свойства и поведение этого объекта, значит, его можно определить и для класса. Такой шаблон, задающий различные классы, называется метаклассом.
    Чтобы представить себе, что такое метакласс, рассмотрим пример некой бюрократической организации. Будем считать, что все классы в такой системе представляют собой строгие инструкции, которые описывают, что нужно сделать, чтобы породить новый объект (например, нанять нового служащего или открыть новый отдел). Как и полагается классам, они описывают все свойства новых объектов (например, зарплату и профессиональный уровень для сотрудников, площадь и имущество для отделов) и их поведение (обязанности служащих и функции подразделений).
    В свою очередь, написание новой инструкции можно строго регламентировать. Скажем, необходимо использовать специальный бланк, придерживаться правил оформления и заполнить все обязательные поля (например, номер инструкции и фамилии ответственных работников). Такая "инструкция инструкций" и будет представлять собой метакласс в ООП.
    Итак, объекты порождаются от классов, а классы - от метакласса. Он, как правило, в системе только один. Но существуют языки программирования, в которых можно создавать и использовать собственные метаклассы, например язык Python. В частности, функциональность метакласса может быть следующая: при формировании класса он будет просматривать список всех методов в классе и, если имя метода имеет вид set_XXX или get_XXX, автоматически создавать поле с именем XXX, если такого не существует.
    Поскольку метакласс сам является классом, то нет никакого смысла в создании "мета-мета-классов".
    В языке Java также есть метакласс. Это класс, который так и называется - Class (описывает классы), он располагается в основной библиотеке java.lang. Виртуальная машина использует его по прямому назначению. Когда загружается очередной .class-файл, содержащий описание нового класса, JVM порождает объект класса Class, который будет хранить его структуру. Таким образом, Java использует концепцию метакласса в самых практических целях. С помощью Class реализована поддержка статических (static) полей и методов. Наконец, этот класс содержит ряд методов, полезных для разработчиков. Они будут рассмотрены в следующих лекциях.

    Методология объектно-ориентированного программирования

    Увеличение размеров программ приводило к необходимости привлечения большего числа программистов, что, в свою очередь, потребовало дополнительных ресурсов для организации их согласованной работы. В процессе разработки приложений заказчик зачастую изменял функциональные требования, что еще более усложняло процесс создания программного обеспечения.
    Но не менее важными оказались качественные изменения, связанные со смещением акцента использования компьютеров. В эпоху "больших машин" основными потребителями программного обеспечения были такие крупные заказчики, как большие производственные предприятия, финансовые компании, государственные учреждения. Стоимость таких вычислительных устройств для небольших предприятий и организаций была слишком высока.
    Позже появились персональные компьютеры, которые имели гораздо меньшую стоимость и были значительно компактнее. Это позволило широко использовать их в малом и среднем бизнесе. Основными задачами в этой области являются обработка данных и манипулирование ими, поэтому вычислительные и расчетно-алгоритмические задачи с появлением персональных компьютеров отошли на второй план. Как показала практика, традиционные методы процедурного программирования не способны справиться ни с нарастающей сложностью программ и их разработки, ни с необходимостью повышения их надежности. Во второй половине 80-х годов возникла настоятельная потребность в новой методологии программирования, которая была бы способна решить весь этот комплекс проблем. Ею стало объектно-ориентированное программирование (ООП).
    После составления технического задания начинается этап проектирования, или дизайна, будущей системы. Объектно-ориентированный подход к проектированию основан на представлении предметной области задачи в виде множества моделей для независимой от языка разработки программной системы на основе ее прагматики.
    Последний термин нуждается в пояснении. Прагматика определяется целью разработки программной системы, например, обслуживание клиентов банка, управление работой аэропорта, обслуживание чемпионата мира по футболу и т.п. В формулировке цели участвуют предметы и понятия реального мира, имеющие отношение к создаваемой системе (см. рисунок 2.2 [3]). При объектно-ориентированном подходе эти предметы и понятия заменяются моделями, т.е. определенными формальными конструкциями.
    Методология объектно-ориентированного программирования
    Рис. 2.2.  Семантика (смысл программы с точки зрения выполняющего ее компьютера) и прагматика (смысл программы с точки зрения ее пользователей) [3].
    Модель содержит не все признаки и свойства представляемого ею предмета или понятия, а только те, которые существенны для разрабатываемой программной системы. Таким образом, модель "беднее", а следовательно, проще представляемого ею предмета или понятия.
    Простота модели по отношению к реальному предмету позволяет сделать ее формальной. Благодаря такому характеру моделей при разработке можно четко выделить все зависимости и операции над ними в создаваемой программной системе. Это упрощает как разработку и изучение (анализ) моделей, так и их реализацию на компьютере.
    Объектно-ориентированный подход обладает такими преимуществами, как:
  • уменьшение сложности программного обеспечения;
  • повышение надежности программного обеспечения;
  • обеспечение возможности модификации отдельных компонентов программного обеспечения без изменения остальных его компонентов;
  • обеспечение возможности повторного использования отдельных компонентов программного обеспечения.

  • Более детально преимущества и недостатки объектно-ориентированного программирования будут рассмотрены в конце лекции, так как для их понимания необходимо знание основных понятий и положений ООП.
    Систематическое применение объектно-ориентированного подхода позволяет разрабатывать хорошо структурированные, надежные в эксплуатации, достаточно просто модифицируемые программные системы. Этим объясняется интерес программистов к объектно-ориентированному подходу и объектно-ориентированным языкам программирования. ООП является одним из наиболее интенсивно развивающихся направлений теоретического и прикладного программирования.

    Методология процедурно-ориентированного программирования

    Появление первых электронных вычислительных машин, или компьютеров, ознаменовало новый этап в развитии техники вычислений. Казалось, достаточно разработать последовательность элементарных действий, каждое из которых можно преобразовать в понятные компьютеру инструкции, и любая вычислительная задача будет решена. Эта идея оказалась настолько жизнеспособной, что долгое время доминировала над всем процессом разработки программ. Появились специализированные языки программирования, созданные для разработки программ, предназначенных для решения вычислительных задач. Примерами таких языков могут служить FOCAL (FOrmula CALculator) и FORTRAN (FORmula TRANslator).
    Основой такой методологии разработки программ являлась процедурная, или алгоритмическая, организация структуры программного кода. Это было настолько естественно для решения вычислительных задач, что целесообразность такого подхода ни у кого не вызывала сомнений. Исходным в данной методологии было понятие алгоритма. Алгоритм - это способ решения вычислительных и других задач, точно описывающий определенную последовательность действий, которые необходимо выполнить для достижения заданной цели. Примерами алгоритмов являются хорошо известные правила нахождения корней квадратного уравнения или системы линейных уравнений.
    При увеличении объемов программ для упрощения их разработки появилась необходимость разбивать большие задачи на подзадачи. В языках программирования возникло и закрепилось новое понятие процедуры. Использование процедур позволило разбивать большие задачи на подзадачи и таким образом упростило написание больших программ. Кроме того, процедурный подход позволил уменьшить объем программного кода за счет написания часто используемых кусков кода в виде процедур и их применения в различных частях программы.
    Как и алгоритм, процедура представляет собой законченную последовательность действий или операций, направленных на решение отдельной задачи. В языках программирования появилась специальная синтаксическая конструкция, которая также получила название процедуры. Например, на языке Pascal описание процедуры выглядит следующим образом:

    Procedure printGreeting(name: String) Begin Print("Hello, "); PrintLn(name); End;

    Назначение данной процедуры - вывести на экран приветствие Hello, Name, где Name передается в процедуру в качестве входного параметра.

    Со временем вычислительные задачи становились все сложнее, а значит, и решающие их программы увеличивались в размерах. Их разработка превратилась в серьезную проблему. Когда программа становится все больше, ее приходится разделять на все более мелкие фрагменты. Основой для такого разбиения как раз и стала процедурная декомпозиция, при которой отдельные части программы, или модули, представляли собой совокупность процедур для решения одной или нескольких задач. Одна из основных особенностей процедурного программирования заключается в том, что оно позволило создавать библиотеки подпрограмм (процедур), которые можно было бы использовать повторно в различных проектах или в рамках одного проекта. При процедурном подходе для визуального представления алгоритма выполнения программы применяется так называемая блок-схема. Соответствующая система графических обозначений была зафиксирована в ГОСТ 19.701-90. Пример блок-схемы изображен на рисунке (рис. 2.1).

    Методология процедурно-ориентированного программирования
    Рис. 2.1.  Пример блок-схемы.

    Появление и интенсивное использование условных операторов и оператора безусловного перехода стало предметом острых дискуссий среди специалистов по программированию. Дело в том, что бесконтрольное применение в программе оператора безусловного перехода goto может заметно осложнить понимание кода. Такие запутанные программы сравнивали с порцией спагетти (bowl of spaghetti), имея ввиду многочисленные переходы от одного фрагмента программы к другому, или, что еще хуже, возврат от конечных операторов программы к начальным. Ситуация казалась настолько драматичной, что многие предлагали исключить оператор goto из языков программирования. Именно с этого времени отсутствие безусловных переходов стали считать хорошим стилем программирования.

    Дальнейшее увеличение программных систем способствовало формированию новой точки зрения на процесс разработки программ и написания программных кодов, которая получила название методологии структурного программирования. Ее основой является процедурная декомпозиция предметной области решаемой задачи и организация отдельных модулей в виде совокупности процедур. В рамках этой методологии получило развитие нисходящее проектирование программ, или проектирование "сверху вниз". Пик популярности идей структурного программирования приходится на конец 70-х - начало 80-х годов.

    В этот период основным показателем сложности разработки программ считался ее размер. Вполне серьезно обсуждались такие оценки сложности программ, как количество строк программного кода. Правда, при этом делались некоторые предположения относительно синтаксиса самих строк, которые должны были соответствовать определенным требованиям. Например, каждая строка кода должна была содержать не более одного оператора. Общая трудоемкость разработки программ оценивалась специальной единицей измерения - "человеко-месяц", или "человеко-год". А профессионализм программиста напрямую связывался с количеством строк программного кода, который он мог написать и отладить в течение, скажем, месяца.


    Procedure printGreeting(name: String) Begin Print("Hello, "); PrintLn(name); End;

    Назначение данной процедуры - вывести на экран приветствие Hello, Name, где Name передается в процедуру в качестве входного параметра.

    Со временем вычислительные задачи становились все сложнее, а значит, и решающие их программы увеличивались в размерах. Их разработка превратилась в серьезную проблему. Когда программа становится все больше, ее приходится разделять на все более мелкие фрагменты. Основой для такого разбиения как раз и стала процедурная декомпозиция, при которой отдельные части программы, или модули, представляли собой совокупность процедур для решения одной или нескольких задач. Одна из основных особенностей процедурного программирования заключается в том, что оно позволило создавать библиотеки подпрограмм (процедур), которые можно было бы использовать повторно в различных проектах или в рамках одного проекта. При процедурном подходе для визуального представления алгоритма выполнения программы применяется так называемая блок-схема. Соответствующая система графических обозначений была зафиксирована в ГОСТ 19.701-90. Пример блок-схемы изображен на рисунке (рис. 2.1).

    Методология процедурно-ориентированного программирования
    Рис. 2.1.  Пример блок-схемы.

    Появление и интенсивное использование условных операторов и оператора безусловного перехода стало предметом острых дискуссий среди специалистов по программированию. Дело в том, что бесконтрольное применение в программе оператора безусловного перехода goto может заметно осложнить понимание кода. Такие запутанные программы сравнивали с порцией спагетти (bowl of spaghetti), имея ввиду многочисленные переходы от одного фрагмента программы к другому, или, что еще хуже, возврат от конечных операторов программы к начальным. Ситуация казалась настолько драматичной, что многие предлагали исключить оператор goto из языков программирования. Именно с этого времени отсутствие безусловных переходов стали считать хорошим стилем программирования.

    Дальнейшее увеличение программных систем способствовало формированию новой точки зрения на процесс разработки программ и написания программных кодов, которая получила название методологии структурного программирования. Ее основой является процедурная декомпозиция предметной области решаемой задачи и организация отдельных модулей в виде совокупности процедур. В рамках этой методологии получило развитие нисходящее проектирование программ, или проектирование "сверху вниз". Пик популярности идей структурного программирования приходится на конец 70-х - начало 80-х годов.

    В этот период основным показателем сложности разработки программ считался ее размер. Вполне серьезно обсуждались такие оценки сложности программ, как количество строк программного кода. Правда, при этом делались некоторые предположения относительно синтаксиса самих строк, которые должны были соответствовать определенным требованиям. Например, каждая строка кода должна была содержать не более одного оператора. Общая трудоемкость разработки программ оценивалась специальной единицей измерения - "человеко-месяц", или "человеко-год". А профессионализм программиста напрямую связывался с количеством строк программного кода, который он мог написать и отладить в течение, скажем, месяца.

    Наследование

    Наследование (inheritance) - это отношение между классами, при котором класс использует структуру или поведение другого класса (одиночное наследование), или других (множественное наследование) классов. Наследование вводит иерархию "общее/частное", в которой подкласс наследует от одного или нескольких более общих суперклассов. Подклассы обычно дополняют или переопределяют унаследованную структуру и поведение.
    В качестве примера можно рассмотреть задачу, в которой необходимо реализовать классы "Легковой автомобиль" и "Грузовой автомобиль". Очевидно, эти два класса имеют общую функциональность. Так, оба они имеют 4 колеса, двигатель, могут перемещаться и т.д. Всеми этими свойствами обладает любой автомобиль, независимо от того, грузовой он или легковой, 5- или 12-местный. Разумно вынести эти общие свойства и функциональность в отдельный класс, например, "Автомобиль" и наследовать от него классы "Легковой автомобиль" и "Грузовой автомобиль", чтобы избежать повторного написания одного и того же кода в разных классах.
    Наследование

    Отношение обобщения обозначается сплошной линией с треугольной стрелкой на конце. Стрелка указывает на более общий класс (класс-предок или суперкласс), а ее отсутствие - на более специальный класс (класс-потомок или подкласс).
    Использование наследования способствует уменьшению количества кода, созданного для описания схожих сущностей, а также способствует написанию более эффективного и гибкого кода.
    В рассмотренном примере применено одиночное наследование. Некоторый класс также может наследовать свойства и поведение сразу нескольких классов. Наиболее популярным примером применения множественного наследования является проектирование системы учета товаров в зоомагазине.
    Все животные в зоомагазине являются наследниками класса "Животное", а также наследниками класса "Товар". Т.е. все они имеют возраст, нуждаются в пище и воде и в то же время имеют цену и могут быть проданы.
    Множественное наследование на диаграмме изображается точно так же, как одиночное, за исключением того, что линии наследования соединяют класс-потомок сразу с несколькими суперклассами.
    Не все объектно-ориентированные языки программирования содержат языковые конструкции для описания множественного наследования.
    В языке Java множественное наследование имеет ограниченную поддержку через интерфейсы и будет рассмотрено в лекции 8.

    Недостатки ООП

    Документирование классов - задача более трудная, чем это было в случае процедур и модулей. Поскольку любой метод может быть переопределен, в документации должно говориться не только о том, что делает данный метод, но и о том, в каком контексте он вызывается. Ведь переопределенные методы обычно вызываются не клиентом, а самим каркасом. Таким образом, программист должен знать, какие условия выполняются, когда вызывается данный метод. Для абстрактных методов, которые пусты, в документации должно говориться о том, для каких целей предполагается использовать переопределяемый метод.
    В сложных иерархиях классов поля и методы обычно наследуются с разных уровней. И не всегда легко определить, какие поля и методы фактически относятся к данному классу. Для получения такой информации нужны специальные инструменты, вроде навигаторов классов. Если конкретный класс расширяется, то каждый метод обычно сокращают перед передачей сообщения базовому классу. Реализация операции, таким образом, рассредотачивается по нескольким классам, и чтобы понять, как она работает, нам приходится внимательно просматривать весь код.
    Методы, как правило, короче процедур, поскольку они осуществляют только одну операцию над данными, зато их намного больше. В коротких методах легче разобраться, но они неудобны тем, что код для обработки сообщения иногда "размазан" по многим маленьким методам.
    Инкапсуляцией данных не следует злоупотреблять. Чем больше логики и данных скрыто в недрах класса, тем сложнее его расширять. Отправной точкой здесь должно быть не то, что клиентам не разрешается знать о тех или иных данных, а то, что клиентам для работы с классом этих данных знать не требуется.
    Многие считают, что ООП является неэффективным. Как же обстоит дело в действительности? Мы должны проводить четкую грань между неэффективностью на этапе выполнения, неэффективностью в смысле распределения памяти и неэффективностью, связанной с излишней универсализацией.

  • Неэффективность на этапе выполнения. В языках типа Smalltalk сообщения интерпретируются во время выполнения программы путем осуществления их поиска в одной или нескольких таблицах и за счет выбора подходящего метода. Конечно, это медленный процесс. И даже при использовании наилучших методов оптимизации Smalltalk-программы в десять раз медленнее оптимизированных C-программ.

    В гибридных языках типа Oberon-2, Object Pascal и C++ отправка сообщения приводит лишь к вызову через указатель процедурной переменной. На некоторых машинах сообщения выполняются лишь на 10% медленнее, чем обычные процедурные вызовы. И поскольку сообщения встречаются в программе гораздо реже других операций, их воздействие на время выполнения влияния практически не оказывает.

    Однако существует другой фактор, который влияет на время выполнения: это инкапсуляция данных. Рекомендуется не предоставлять прямой доступ к полям класса, а выполнять каждую операцию над данными через методы. Такая схема приводит к необходимости выполнения процедурного вызова каждый раз при доступе к данным. Однако если инкапсуляция используется только там, где она необходима (т.е. в тех случаях, когда это становится преимуществом), то замедление вполне приемлемое.
  • Неэффективность в смысле распределения памяти. Динамическое связывание и проверка типа на этапе выполнения требуют по ходу работы информации о типе объекта. Такая информация хранится в дескрипторе типа и он выделяется один на класс. Каждый объект имеет невидимый указатель на дескриптор типа для своего класса. Таким образом, в объектно-ориентированных программах необходимая дополнительная память выражается в одном указателе для объекта и в одном дескрипторе типа для класса.


  • Излишняя универсальность. Неэффективность также может означать, что в программе реализованы избыточные возможности. В библиотечном классе часто содержится больше методов, чем это реально необходимо. А поскольку лишние методы не могут быть удалены, они становятся мертвым грузом. Это не влияет на время выполнения, но сказывается на размере кода.

    Одно из возможных решений - строить базовый класс с минимальным числом методов, а затем уже реализовывать различные расширения этого класса, которые позволят нарастить функциональность. Другой подход - дать компоновщику возможность удалять лишние методы. Такие интеллектуальные компоновщики уже существуют для различных языков и операционных систем.



  • Но нельзя утверждать, что ООП неэффективно. Если классы используются лишь там, где это действительно необходимо, то потеря эффективности из-за повышенного расхода памяти и меньшей производительности незначительна. Кроме того, надежность программного обеспечения и быстрота его написания часто бывает важнее, чем производительность.

    Объекты

    По определению будем называть объектом понятие, абстракцию или любой предмет с четко очерченными границами, имеющий смысл в контексте рассматриваемой прикладной проблемы. Введение объектов преследует две цели:
  • понимание прикладной задачи (проблемы);
  • введение основы для реализации на компьютере.

  • Примеры объектов: форточка, Банк "Империал", Петр Сидоров, дело № 7461, сберкнижка и т.д.
    Каждый объект имеет определенное время жизни. В процессе выполнения программы, или функционирования какой-либо реальной системы, могут создаваться новые объекты и уничтожаться уже существующие.
    Гради Буч дает следующее определение объекта:
    Объект - это мыслимая или реальная сущность, обладающая характерным поведением и отличительными характеристиками и являющаяся важной в предметной области [2].
    Каждый объект имеет состояние, обладает четко определенным поведением и уникальной идентичностью.

    Первое приложение Java

    Назад Вперед
    Как мы уже говорили, приложение Java может выполняться под управлением специального интерпретатора, работающего в рамках отдельного процесса, либо под управлением браузера. В первом случае мы имеем дело с автономным приложением Java, в втором - с аплетом.
    Изучение программирования на Java мы начнем с создания простейшего автономного приложения. В следующей статье мы расскажем о том, как сделать аплет и встроить его в документ HTML.
    В качестве инструментального средства для разработки автономных приложений и аплетов Java мы будем использовать интегрированную систему разработки Java WorkShop. Она доступна для операционных систем Windows 95, Windows NT, Solaris (платформы SPARC и Intel).

    Первое приложение JavaПервое приложение Java Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Первое приложение Java
    printmenus();
    Первое приложение JavaПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Первое приложение Java»


    Первое приложение JavaSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Первое приложение Java»


    Первое приложение JavaКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Первое приложение Java»


    Первое приложение JavaГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Первое приложение Java»


    Первое приложение JavaОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Первое приложение Java»


    Первое приложение JavaПроекты
    События
    Lab Downloads
    Посмотреть все Первое приложение Java»


    Первое приложение Java

    Полиморфизм

    Полиморфизм является одним из фундаментальных понятий в объектно-ориентированном программировании наряду с наследованием и инкапсуляцией. Слово "полиморфизм" греческого происхождения и означает "имеющий много форм". Чтобы понять, что оно означает применительно к объектно-ориентированному программированию, рассмотрим пример.
    Предположим, мы хотим создать векторный графический редактор, в котором нам нужно описать в виде классов набор графических примитивов - Point, Line, Circle, Box и т.д. У каждого из этих классов определим метод draw для отображения соответствующего примитива на экране.
    Очевидно, придется написать код, который при необходимости отобразить рисунок будет последовательно перебирать все примитивы, на момент отрисовки находящиеся на экране, и вызывать метод draw у каждого из них. Человек, незнакомый с полиморфизмом, вероятнее всего, создаст несколько массивов (отдельный массив для каждого типа примитивов) и напишет код, который последовательно переберет элементы из каждого массива и вызовет у каждого элемента метод draw. В результате получится примерно следующий код:
    … //создание пустого массива, который может // содержать объекты Point с максимальным // объемом 1000 Point[] p = new Point[1000];
    Line[] l = new Line[1000]; Circle[] c = new Circle[1000]; Box[] b = new Box[1000]; … // предположим, в этом месте происходит // заполнение всех массивов соответствующими // объектами … for(int i = 0; i < p.length;i++) { //цикл с перебором всех ячеек массива. //вызов метода draw() в случае, // если ячейка не пустая. if(p[i]!=null) p.draw(); }
    for(int i = 0; i < l.length;i++) { if(l[i]!=null) l.draw(); }
    for(int i = 0; i < c.length;i++) { if(c[i]!=null) c.draw(); }
    for(int i = 0; i < b.length;i++) { if(b[i]!=null) b.draw(); } …
    Недостатком написанного выше кода является дублирование практически идентичного кода для отображения каждого типа примитивов. Также неудобно то, что при дальнейшей модернизации нашего графического редактора и добавлении возможности рисовать новые типы графических примитивов, например Text, Star и т.д., при таком подходе придется менять существующий код и добавлять в него определения новых массивов, а также обработку содержащихся в них элементов.
    Используя полиморфизм, мы можем значительно упростить реализацию подобной функциональности. Прежде всего, создадим общий родительский класс для всех наших классов. Пусть таким классом будет Point. В результате получим иерархию классов, которая изображена на рисунке 2.3.
    У каждого из дочерних классов метод draw переопределен таким образом, чтобы отображать экземпляры каждого класса соответствующим образом.
    Для описанной выше иерархии классов, используя полиморфизм, можно написать следующий код:

    … Point p[] = new Point[1000]; p[0] = new Circle(); p[1] = new Point(); p[2] = new Box(); p[3] = new Line(); … for(int i = 0; i < p.length;i++) { if(p[i]!=null) p[i].draw(); } …

    В описанном выше примере массив p[] может содержать любые объекты, порожденные от наследников класса Point. При вызове какого-либо метода у любого из элементов этого массива будет выполнен метод того объекта, который содержится в ячейке массива. Например, если в ячейке p[0] находится объект Circle, то при вызове метода draw следующим образом:

    p[0].draw()

    нарисуется круг, а не точка.

    В заключение приведем формальное определение полиморфизма.

    Полиморфизм (polymorphism) - положение теории типов, согласно которому имена (например, переменных) могут обозначать объекты разных (но имеющих общего родителя) классов. Следовательно, любой объект, обозначаемый полиморфным именем, может по-своему реагировать на некий общий набор операций [2].

    В процедурном программировании тоже существует понятие полиморфизма, которое отличается от рассмотренного механизма в ООП. Процедурный полиморфизм предполагает возможность создания нескольких процедур или функций с одним и тем же именем, но разным количеством или различными типами передаваемых параметров. Такие одноименные функции называются перегруженными, а само явление - перегрузкой (overloading). Перегрузка функций существует и в ООП и называется перегрузкой методов.

    Полиморфизм
    Рис. 2.3.  Пример иерархии классов.

    Примером использования перегрузки методов в языке Java может служить класс PrintWriter, который применяется, в частности, для вывода сообщений на консоль. Этот класс имеет множество методов println, которые различаются типами и/или количеством входных параметров. Вот лишь несколько из них:

    void println() // переход на новую строку void println(boolean x) // выводит значение булевской // переменной (true или false) void println(String x) // выводит строку - значение // текстового параметра.

    Определенные сложности возникают при вызове перегруженных методов. В Java существуют специальные правила, которые позволяют решать эту проблему. Они будут рассмотрены в соответствующей лекции.

    Поведение

    Для каждого объекта существует определенный набор действий, которые с ним можно произвести. Например, возможные действия с некоторым файлом операционной системы ПК:
  • создать;
  • открыть;
  • читать из файла;
  • писать в файл;
  • закрыть;
  • удалить.

  • Результат выполнения действий зависит от состояния объекта на момент совершения действия, т.е. нельзя, например, удалить файл, если он открыт кем-либо (заблокирован). В то же время действия могут менять внутреннее состояние объекта - при открытии или закрытии файла свойство "открыт" принимает значения "да" или "нет", соответственно.
    Программа, написанная с использованием ООП, обычно состоит из множества объектов, и все эти объекты взаимодействуют между собой. Обычно говорят, что взаимодействие между объектами в программе происходит посредством передачи сообщений между ними.
    В терминологии объектно-ориентированного подхода понятия "действие", "сообщение" и "метод" являются синонимами. Т.е. выражения "выполнить действие над объектом", "вызвать метод объекта" и "послать сообщение объекту для выполнения какого-либо действия" эквивалентны. Последняя фраза появилась из следующей модели. Программу, построенную по технологии ООП, можно представить себе как виртуальное пространство, заполненное объектами, которые условно "живут" некоторой жизнью. Их активность проявляется в том, что они вызывают друг у друга методы, или посылают друг другу сообщения. Внешний интерфейс объекта, или набор его методов,- это описание того, какие сообщения он может принимать.
    Поведение (behavior) - действия и реакции объекта, выраженные в терминах передачи сообщений и изменения состояния; видимая извне и воспроизводимая активность объекта [2].

    Состояние

    Рассмотрим пример. Любой человек может находиться в некотором положении (состоянии): стоять, сидеть, лежать, и - в то же время совершать какие либо действия.
    Например, человек может прыгать, если он стоит, и не может - если он лежит, для этого ему потребуется сначала встать. Также в объектно-ориентированном программировании состояние объекта может определяться наличием или отсутствием связей между моделируемым объектом и другими объектами. Более подробно все возможные связи между объектами будут рассмотрены в разделе "Типы отношений между классами".
    Например, если у человека есть удочка (у него есть связь с объектом "Удочка"), он может ловить рыбу, а если удочки нет, то такое действие невозможно. Из этих примеров видно, что набор действий, которые может совершать человек, зависит от параметров объекта, его моделирующего.
    Для рассмотренных выше примеров такими характеристиками, или атрибутами, объекта "Человек" являются:
  • текущее положение человека (стоит, сидит, лежит);
  • наличие удочки (есть или нет).

  • В конкретной задаче могут появиться и другие свойства, например, физическое состояние, здоровье (больной человек обычно не прыгает).
    Состояние (state) - совокупный результат поведения объекта: одно из стабильных условий, в которых объект может существовать, охарактеризованных количественно; в любой момент времени состояние объекта включает в себя перечень (обычно статический) свойств объекта и текущие значения (обычно динамические) этих свойств [2].

    Создание нового проекта

    Как и многие другие интегрированные системы разработки программного обеспечения, система Java WorkShop использует концепцию проектов. Под проектом здесь понимается совокупность файлов и параметров, описанные в специальном файле проектов.
    Всеми проектами в Java WorkShop управляет менеджер проектов. Чтобы открыть его окно, показанное на рис. 3, выберите строку Show Project Manager из меню Project главного окна Java WorkShop.
    Создание нового проекта
    Рис. 3. Окно Java WorkShop Project Manager
    Все проекты хранятся в портфелях (portfolios). В процессе установки автоматически создается ваш личный портфель personal, который находится в папке My Portfolios. В этом портфеле есть проекты Checkers, CardFile, JellyBeanExample и Performance Example. При создании нового проекта мы добавим его в портфель personal, хотя в дальнейшем вы сможете создавать новые портфели.
    Прежде чем выполнять какие либо операции над портфелем или проектом, нужно сделать этот портфель или проект текущим. Чтобы выбрать текущий портфель или проект, следует сделать двойной щелчок левой клавишей мыши по соответствующему названию. Строка названия будет выделена синим цветом.
    Для создания проекта HelloJava выберите из меню File окна Java WorkShop Project Manager строку New, а затем в меню второго уровня строку Project. В результате будет запущен мастер создания проектов, первая диалоговая панель которого показана на рис. 4.

    Создание нового проекта Рис. 4. Первая диалоговая панель мастера проектов
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    В поле Please, name this project введите название создаваемого проекта HelloJava, затем включите переключатели Standalone и No GUI. Первый из этих переключателей задает тип проекта (автономное приложение Java), второй указывает, что в проекте не используются средства автоматизированного проектирования пользовательского интерфейса.
    Далее нажмите кнопку Next. Вы окажитесь в диалоговой панели Create Project, показанной на рис. 5.

    Создание нового проекта Рис. 5. Диалоговая панель Create Project
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению
    <
    В верхней части этой панели вы должны ввести путь к каталогу, в котором будут создаваться файлы проекта. Затем следует включить переключатель No, указывая тем самым, что каталог проектов пуст и в нем нужно создать новые файлы. Существует возможность создания проектов на базе уже имеющихся файлов, однако пока мы этого делать не будем.

    Теперь все готово к созданию проекта и вы можете нажать кнопку Finish. Мастер проектов создаст все необходимые файлы и сделает новый проект текущим. Основной файл проекта окажется загруженным в главное окно приложения Java WorkShop (рис. 6). Здесь вы можете его редактировать.

    Создание нового проекта Рис. 6. Основной файл созданного проекта загружен в окно приложения Java WorkShop

    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению
    Если теперь вы раскроете окно Java WorkShop Project Manager, то увидите, что в портфеле personal появился новый проект HelloJava. Раскрыв его, можно увидеть включенные в него файлы (рис. 7).

    Создание нового проекта

    Рис. 7. Файлы проекта HelloJava.

    Наш проект содержит только один файл с именем HelloJava.java. Отредактируйте этот файл, добавив в него следующую строку:

    System.out.println("Hello, Java!");

    Новый вариант исходного текста показан на рис. 8.

    Создание нового проекта Рис. 8. Измененный исходный текст приложения HelloJava

    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению
    Теперь выберите из меню Build главного окна Java WorkShop строку Build All. Через несколько мгновений исходные тексты приложения будут оттранслированы. Результат вы сможете увидеть нас странице Build блокнота, расположенного в нижней части окна Java WorkShop.

    Чтобы запустить приложение, выберите из меню Project строку Run. На экране появится консоль приложения с сообщением "Hello, Java!" (рис. 9).

    Создание нового проекта

    Рис. 9. Работа приложения Hello, Java!

    Назад Вперед

    Создание нового проектаСоздание нового проекта Контакты

    О компании

    Новости

    Вакансии

    Правовые аспекты

    Условия использования

    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.

    Создание нового проекта

    Типы отношений между классами

    Как правило, любая программа, написанная на объектно-ориентированном языке, представляет собой некоторый набор связанных между собой классов. Можно провести аналогию между написанием программы и строительством дома. Подобно тому, как стена складывается из кирпичей, компьютерная программа с использованием ООП строится из классов. Причем эти классы должны иметь представление друг о друге, для того чтобы сообща выполнять поставленную задачу.
    Возможны следующие связи между классами в рамках объектной модели (приводятся лишь наиболее простые и часто используемые виды связей, подробное их рассмотрение выходит за рамки этой ознакомительной лекции):
  • агрегация (Aggregation);

  • ассоциация (Association);

  • наследование (Inheritance);

  • метаклассы (Metaclass).


  • Уникальность

    Уникальность - это то, что отличает объект от других объектов. Например, у вас может быть несколько одинаковых монет. Даже если абсолютно все их свойства (атрибуты) одинаковы (год выпуска, номинал и т.д.) и при этом вы можете использовать их независимо друг от друга, они по-прежнему остаются разными монетами.
    В машинном представлении под параметром уникальности объекта чаще всего понимается адрес размещения объекта в памяти.
    Identity (уникальность) объекта состоит в том, что всегда можно определить, указывают две ссылки на один и тот же объект или на разные объекты. При этом два объекта могут во всем быть похожими, их образ в памяти может представляться одинаковыми последовательностями байтов, но, тем не менее, их Identity может быть различна.
    Наиболее распространенной ошибкой является понимание уникальности как имени ссылки на объект. Это неверно, т.к. на один объект может указывать несколько ссылок, и ссылки могут менять свои значения (ссылаться на другие объекты).
    Итак, уникальность (identity) - свойство объекта; то, что отличает его от других объектов (автор не согласен с переводом русского издания [2], поэтому здесь приводится авторский перевод).

    Установка Java WorkShop

    Назад Вперед
    Для установки Java WorkShop вы должны вставить дистрибутивный диск в устройство чтения CD-ROM. Программа установки запустится автоматически. Если этого не произошло (например, потому что автоматический запуск заблокирован), следует запустить программу установки вручную.
    Если вы работаете в среде Windows 95 или Windows NT, вам нужно запустить программу setup.exe из каталога Win32. На дистрибутивном диске Java WorkShop есть также установочные каталоги для других платформ.
    Процедура установки очень проста. Достаточно следовать инструкциям, которые появляются на экране. Единственное, что необходимо выбрать - это путь к каталогу, в который будут скопированы файлы Java WorkShop. При этом следует учесть, что для установки Java WorkShop в среде Windows 95 или Windows NT требуется примерно 50 Мбайт дисковой памяти.
    Назад Вперед

    Установка Java WorkShopУстановка Java WorkShop Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Установка Java WorkShop
    printmenus();
    Установка Java WorkShopПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Установка Java WorkShop»


    Установка Java WorkShopSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Установка Java WorkShop»


    Установка Java WorkShopКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Установка Java WorkShop»


    Установка Java WorkShopГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Установка Java WorkShop»


    Установка Java WorkShopОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Установка Java WorkShop»


    Установка Java WorkShopПроекты
    События
    Lab Downloads
    Посмотреть все Установка Java WorkShop»


    Установка Java WorkShop

    В этой лекции мы рассказали

    В этой лекции мы рассказали об объектно-ориентированном подходе к разработке ПО, а также о том, что послужило предпосылками к его появлению и сделало его популярным. Были рассмотрены ключевые понятия ООП - объект и класс. Далее были описаны основные свойства объектной модели - инкапсуляция, наследование, полиморфизм. Основными видами отношений между классами являются наследование, ассоциация, агрегация, метакласс. Также были описаны правила изображения классов и связей между ними на языке UML.

    Запуск Java WorkShop

    Назад Вперед
    После завершения установки следует извлечь дистрибутивный компакт-диск и запустить Java WorkShop, воспользовавшись пиктограммой, созданной на рабочем столе операционной системы.
    Когда система разработки Java WorkShop запускается впервые, она требует ввести серийный номер. Этот номер есть на вкладыше в конверте дистрибутивного компакт-диска. В том случае, если вы располагаете демонстрационной версией Java WorkShop, серийный номер можно не вводить. При этом вы сможете пользоваться Java WorkShop бесплатно в течении 30 дней.
    После запуска Java WorkShop на экране появится главное окно Java WorkShop (рис.1), а также окно Java WorkShop Startup (рис. 2).

    Запуск Java WorkShop Рис. 1. Главное окно интегрированной системы разработки Java WorkShop
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению


    Запуск Java WorkShop Рис. 2. Окно Java WorkShop Startup
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    Если раньше вы никогда не работали с Java WorkShop, окно Java WorkShop Startup поможет вам освоиться с основными процедурами. Нажав соответствующую кнопку, вы можете создать свой первый проект, открыть пример проекта, подготовленного для вас специалистами Sun Microsystems, ознакомиться с электронным руководством или справочной системой, а также открыть любой существующий проект.
    После приобретения небольшой практики можно включить переключатель Don't show me this window at startup, и окно Java WorkShop Startup перестанет появляться всякий раз, когда вы запускаете Java WorkShop.

    Программирование на Java

    Анализ программы

    Компилятор, анализируя программу, сразу разделяет ее на:

  • пробелы (white spaces);
  • комментарии (comments);
  • основные лексемы (tokens).


  • Арифметические операции

    Наряду с четырьмя обычными арифметическими операциями +, -, *, /, существует оператор получения остатка от деления %, который может быть применен как к целочисленным аргументам, так и к дробным.
    Работа с целочисленными аргументами подчиняется простым правилам. Если делится значение 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.

    Битовые операции

    Прежде чем переходить к битовым операциям, необходимо уточнить, каким именно образом целые числа представляются в двоичном виде. Конечно, для неотрицательных величин это практически очевидно:
    0 0 1 1 2 10 3 11 4 100 5 101
    и так далее. Однако как представляются отрицательные числа? Во-первых, вводят понятие знакового бита. Первый бит начинает отвечать за знак, а именно 0 означает положительное число, 1 – отрицательное. Но не следует думать, что остальные биты остаются неизменными. Например, если рассмотреть 8-битовое представление:
    -1 10000001 // это НЕВЕРНО! -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.

    Целочисленные литералы

    Целочисленные литералы позволяют задавать целочисленные значения в десятеричном, восьмеричном и шестнадцатеричном виде. Десятеричный формат традиционен и ничем не отличается от правил, принятых в других языках. Значения в восьмеричном виде начинаются с нуля, и, конечно, использование цифр 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 до 017777777777 (=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

    Дополнение. Работа с операторами

    Рассмотрим некоторые детали использования операторов в Java. Здесь будут описаны подробности, относящиеся к работе самих операторов. В следующей лекции детально рассматриваются особенности, возникающие при использовании различных типов данных (например, значение операции 1/2 равно 0, а 1/2. равно 0.5).

    Дробные литералы

    Дробные литералы представляют собой числа с плавающей десятичной точкой. Правила записи таких чисел такие же, как и в большинстве современных языков программирования.
    Примеры:
    3.14 2. .5 7e10 3.1E-20
    Таким образом, дробный литерал состоит из следующих составных частей:
  • целая часть;
  • десятичная точка (используется ASCII-символ точка);
  • дробная часть;
  • показатель степени (состоит из латинской ASCII-буквы E в произвольном регистре и целого числа с опциональным знаком + или -);
  • окончание-указатель типа.

  • Целая и дробная части записываются десятичными цифрами, а указатель типа (аналог указателя 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+5F // 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)). В этой спецификации описаны не только конечные дробные величины, но и еще несколько особых значений, а именно:
  • положительная и отрицательная бесконечности (positive/negative infinity);
  • значение "не число", Not-a-Number, сокращенно NaN;
  • положительный и отрицательный нули.


  • Для этих значений нет специальных обозначений. Чтобы получить такие величины, необходимо либо произвести арифметическую операцию (например, результатом деления ноль на ноль 0.0/0.0 является NaN), либо обратиться к константам в классах Float и Double, а именно POSITIVE_INFINITY, NEGATIVE_INFINITY и NaN. Более подробно работа с этими особенными значениями рассматривается в следующей лекции.

    Типы данных накладывают ограничения на возможные значения литералов, как и для целочисленных типов. Максимальное положительное конечное значение дробного литерала:

  • для float: 3.40282347e+38f
  • для double: 1.79769313486231570e+308


  • Кроме того, для дробных величин становится важным еще одно предельное значение – минимальное положительное ненулевое значение:

  • для float: 1.40239846e-45f
  • для double: 4.94065645841246544e-324


  • Попытка указать литерал со слишком большим абсолютным значением (например, 1e40F) приведет к ошибке компиляции. Такая величина должна представляться бесконечностью. Аналогично, указание литерала со слишком малым ненулевым значением (например, 1e-350) также приводит к ошибке. Это значение должно быть округлено до нуля. Однако если округление приводит не к нулю, то компилятор произведет его сам:

    // ошибка, выражение должно быть округлено до 0 0.00000000000000000000000000000000000000000001f // ошибки нет, компилятор сам округляет до 1 1.00000000000000000000000000000000000000000001f

    Стандартных возможностей вводить дробные значения не в десятичной системе в Java нет, однако классы Float и Double предоставляют много вспомогательных методов, в том числе и для такой задачи.

    Идентификаторы

    Идентификаторы – это имена, которые даются различным элементам языка для упрощения доступа к ним. Имена имеют пакеты, классы, интерфейсы, поля, методы, аргументы и локальные переменные (все эти понятия подробно рассматриваются в следующих лекциях). Идентификаторы можно записывать символами Unicode, то есть на любом удобном языке. Длина имени не ограничена.
    Идентификатор состоит из букв и цифр. Имя не может начинаться с цифры. Java-буквы, используемые в идентификаторах, включают в себя ASCII-символы A-Z (\u0041-\u005a), a-z (\u0061-\u007a), а также знаки подчеркивания _ (ASCII underscore, \u005f) и доллара $ (\u0024). Знак доллара используется только при автоматической генерации кода (чтобы исключить случайное совпадение имен), либо при использовании каких-либо старых библиотек, в которых допускались имена с этим символом. Java-цифры включают в себя обычные ASCII-цифры 0-9 (\u0030-\u0039).
    Для идентификаторов не допускаются совпадения с зарезервированными словами (это ключевые слова, булевские литералы true и false и null-литерал null). Конечно, если 2 идентификатора включают в себя разные буквы, которые одинаково выглядят (например, латинская и русская буквы A), то они считаются различными.
    В этой лекции уже применялись следующие идентификаторы:
    Character, a, b, c, D, x1, x2, Math, sqrt, x, y, i, s, PI, getRadius, circle, getAbs, calculate, condition, getWidth, getHeight, java, lang, String
    Также допустимыми являются идентификаторы:
    Computer, COLOR_RED, _, aVeryLongNameOfTheMethod

    Иерархия классов

    Заглянем в документацию. Выберите из меню Help главного окна приложения Java WorkShop строку Java API Documentation. На экране появится окно браузера, встроенного в Java WorkShop. С помощью этого браузера вы сможете просматривать содержимое справочной системы.
    В разделе Java API Packages выберите библиотеку классов java.applet, а затем в разделе Class Index - строку Applet. Вы увидите иерархию классов:
    java.lang.Object | +---java.awt.Component | +---java.awt.Container | +---java.awt.Panel | +---java.applet.Applet
    Из этой иерархии видно, что класс java.applet.Applet произошел от класса java.awt.Panel. Этот класс, в свою очередь, определен в библиотеке классов java.awt и произошел от класса java.awt.Container.
    Продолжим наши исследования. В классе java.awt.Container снова нет метода paint, но сам этот класс создан на базе класса java.awt.Component.
    Но и здесь метода paint нет. Этот метод определен в классе java.awt.Component, который, в свою очередь, произошел от класса java.lang.Object и реализует интерфейс java.awt.image.ImageObserver.
    Таким образом мы проследили иерархию классов от класса java.applet.Applet, на базе которого создан наш аплет, до класса java.lang.Object, который является базовым для всех классов в Java.
    Метод paint определен в классе java.awt.Component, но так как этот класс является базовым для класса Applet и для нашего класса HelloApplet, мы можем переопределить метод paint.

    Исходный текст аплета

    Назад Вперед
    Полный исходный текст аплета, созданный автоматически мастером проектов Java WorkShop, мы представили в листинге 1.
    Листинг 1. Файл HelloApplet.java
    import java.applet.Applet; public class HelloApplet extends Applet { /** * Initializes the applet. You never need to * call this directly; it is * called automatically by the system once the * applet is created. */ public void init() {}
    /** * Called to start the applet. You never need * to call this directly; it * is called when the applet's * document is visited. */ public void start() {}
    /** * Called to stop the applet. This is called * when the applet's document is * no longer on the screen. It is guaranteed * to be called before destroy() * is called. You never need to * call this method directly */ public void stop() {}
    /** * Cleans up whatever resources are being held. * If the applet is active * it is stopped. */ public void destroy() {} }
    Из-за обилия комментариев вы можете подумать, что исходный текст аплета, который ничего не делает, слишком сложный. Однако это вовсе не так. Вот что получится, если мы уберем все комментарии:
    import java.applet.Applet; public class HelloApplet extends Applet { public void init() {} public void start() {} public void stop() {} public void destroy() {} }
    Исходный текст нашего аплета начинается со строки, подключающей оператором import библиотеку классов java.applet.Applet.
    Оператор import должен располагаться в файле исходного текста перед другими операторами (за исключением операторов комментария). В качестве параметра оператору import передается имя подключаемого класса из библиотеки классов. Если же необходимо подключить все классы данной библиотеки, вместо имени класса указывается символ "*".
    Напомним, что библиотека java.applet.Applet содержит классы, необходимые для создания аплетов, то есть разновидности приложений Java, встраиваемых в документы HTML и работающих под управлением браузера Internet.
    Еще одна библиотека классов, которая нам скоро понадобится, это java.awt. С ее помощью аплет может выполнять в своем окне рисование различных изображений или текста. Преимущества данного метода перед использованием для рисования традиционного программного интерфейса операционной системы заключаются в том, что он работает на любой компьютерной платформе.

    Далее в исходном тексте аплета определяется класс типа public с именем HelloApplet. Напомним, что это имя должно обязательно совпадать с именем файла, содержащего исходный текст этого класса.

    public class HelloApplet extends Applet { . . . }

    Определенный нами класс HelloApplet с помощью ключевого слова extends наследуется от класса Applet. При этом методам класса HelloApplet становятся доступными все методы и данные класса, за исключением определенных как private. Класс Applet определен в библиотеке классов java.applet.Applet, которую мы подключили оператором import.

    Назад Вперед

    Исходный текст аплетаИсходный текст аплета Контакты

    О компании

    Новости

    Вакансии

    Правовые аспекты

    Условия использования

    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.

    Исходный текст аплета

    printmenus();

    Исходный текст аплетаПрограммные продукты

    Рабочие станции и тонкие клиенты

    Серверы

    Системы хранения данных

    Посмотреть все Исходный текст аплета»

    Исходный текст аплетаSolaris 10

    Java 2 Standard Edition

    Developer Tools

    Top Downloads

    New Downloads

    Патчи и обновления

    Посмотреть все Исходный текст аплета»

    Исходный текст аплетаКаталог решений

    Истории успеха

    The Sun Grid

    Партнерские программы

    Посмотреть все Исходный текст аплета»

    Исходный текст аплетаГарантийное обслуживание

    Программы SunSpectrum

    Консалтинг

    Услуги инсталляции

    Поддержка ПО

    Посмотреть все Исходный текст аплета»

    Исходный текст аплетаОписание курсов

    Сертификация

    Авторизованные учебные центры

    Посмотреть все Исходный текст аплета»

    Исходный текст аплетаПроекты

    События

    Lab Downloads

    Посмотреть все Исходный текст аплета»

    Исходный текст аплета

    Исходный текст документа HTML

    Назад Вперед
    Кроме файла исходного текста аплета мастер проектов создал файл документа HTML HelloApplet.tmp.html, представленный в листинге 2.
    Листинг 2. Файл HelloApplet.tmp.html

    С помощью оператора наш аплет встраивается в этот документ. Оператор используется в паре с оператором и имеет следующие параметры:

    Параметр Описание
    ALIGN Выравнивание окна аплета относительно окружающего его текста. Возможны следующие значения:
    LEFT - выравнивание влево относительно окружающего текста;
    CENTER - центрирование;
    RIGHT - выравнивание вправо относительно окружающего текста;
    TOP - выравнивание по верхней границе;
    MIDDLE - центрирование по вертикали;
    BOTTOM - выравнивание по нижней границе
    ALT С помощью этого параметра можно задать текст, который будет отображаться в окне аплета в том случае, если браузер не может работать с аплетами Java
    CODE Имя двоичного файла, содержащего байт-код аплета. По умолчанию путь к этому файлу указывается относительно каталога с файлом HTML, в который встроен аплет. Такое поведение может быть изменено параметром CODEBASE
    CODEBASE Базовый адрес URL аплета, то есть путь к каталогу, содержащему аплет
    HEIGHT Ширина окна аплета в пикселах
    WIDTH Высота окна аплета в пикселах
    HSPACE Зазор слева и справа от окна аплета
    VSPACE Зазор сверху и снизу от окна аплета
    NAME Идентификатор аплета, который может быть использован другими аплетами, расположенными в одном и том же документе HTML, а также сценариями JavaScript
    TITLE Строка заголовка

    Дополнительно между операторами и вы можете задать параметры аплета. Для этого используется оператор , который мы рассмотрим позже.

    Изменяем исходный текст аплета

    Назад Вперед
    Теперь давайте попробуем немного изменить исходный текст аплета, чтобы заставить его рисовать в своем окне текстовую строку "Hello, Java world".
    Вначале измените исходный текст так, как это показано на рис. 6.

    Изменяем исходный текст аплета Рис. 6. Измененный исходный текст аплета HelloApplet
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    Здесь мы намеренно внесли в исходный текст ошибку, чтобы показать, как Java WorkShop отреагирует на нее. Как видно из рисунка, сообщение об ошибке отображается на странице блокнота с названием Build. Текст сообщения гласит, что компилятор не смог найти определение класса Graphics, на который есть ссылка в девятой строке.
    Добавим строку импортирования класса java.awt.*, как это показано в листинге 3.
    Листинг 3. Файл HelloApplet.java (новый вариант)
    import java.applet.Applet; import java.awt.*;
    public class HelloApplet extends Applet { public String getAppletInfo() { return "HelloJava Applet"; } public void paint(Graphics g) { g.drawString("Hello, Java world!", 20, 20); } }
    Теперь исходный текст аплета транслируется без ошибок (рис. 7).

    Изменяем исходный текст аплета Рис. 7. Измененный исходный текст успешно оттранслирован
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    Если запустить аплет на выполнение, в его окне будет нарисована строка "Hello, Java world" (рис. 8).
    Изменяем исходный текст аплета
    Рис. 8. Теперь наш аплет "умеет" рисовать в своем окне текстовые строки
    Назад Вперед

    Изменяем исходный текст аплетаИзменяем исходный текст аплета Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Изменяем исходный текст аплета
    printmenus();
    Изменяем исходный текст аплетаПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Изменяем исходный текст аплета»


    Изменяем исходный текст аплетаSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Изменяем исходный текст аплета»


    Изменяем исходный текст аплетаКаталог решений
    Истории успеха
    The Sun Grid

    Ключевые слова

    Ключевые слова – это зарезервированные слова, состоящие из 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 часто считают ключевыми словами (возможно, потому, что многие средства разработки подсвечивают их таким же образом), однако это именно литералы.
    Значение всех ключевых слов будет рассматриваться в следующих лекциях.

    Кодировка

    Технология 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-символов.

    Комментарии не

    Комментарии не влияют на результирующий бинарный код и используются только для ввода пояснений к программе.
    В 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. Вызов метода getRadius() теперь закомментирован и не будет произведен, переменная s всегда будет принимать значение 2 PI. Завершает строку строчный комментарий.
    Комментарии не могут находиться в символьных и строковых литералах, идентификаторах (эти понятия подробно рассматриваются далее в этой лекции). Следующий пример содержит случаи неправильного применения комментариев:
    // В этом примере текст /*…*/ станет просто // частью строки s String s = "text/*just text*/"; /* Следующая строка станет причиной ошибки при компиляции, так как комментарий разбил имя метода getRadius() */ circle.get/*comment*/Radius();
    А такой код допустим:
    // Комментарий может разделять вызовы функций: cirle./*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;
    В этом примере закомментировано выражение if-else и оператор сложения +2*x.
    Как уже говорилось выше, комментарии можно писать символами Unicode, то есть на любом языке, удобном разработчику.
    Кроме этого, существует особый вид блочного комментария – комментарий разработчика. Он применяется для автоматического создания документации кода. В стандартную поставку JDK, начиная с версии 1.0, входит специальная утилита javadoc. На вход ей подается исходный код классов, а на выходе получается удобная документация в HTML-формате, которая описывает все классы, все их поля и методы. При этом активно используются гиперссылки, что существенно упрощает изучение программы (например, читая описание метода, можно с помощью одного нажатия мыши перейти на описание типов, используемых в качестве аргументов или возвращаемого значения). Однако понятно, что одного названия метода и перечисления его аргументов недостаточно для понимания его работы. Необходимы дополнительные пояснения от разработчика.
    Комментарий разработчика записывается так же, как и блочный. Единственное различие в начальной комбинации символов – для документации комментарий необходимо начинать с /**. Например:


    /** * Вычисление модуля целого числа. * Этот метод возвращает * абсолютное значение аргумента x. */ int getAbs(int x) { if (x>=0) return x; else return -x; }
    Первое предложение должно содержать краткое резюме всего комментария. В дальнейшем оно будет использовано как пояснение этой функции в списке всех методов класса (ниже будут описаны все конструкции языка, для которых применяется комментарий разработчика).
    Поскольку в результате создается HTML-документация, то и комментарий необходимо писать по правилам HTML. Допускается применение тегов, таких как и . Однако теги заголовков с

    по

    и
    использовать нельзя, так как они активно применяются javadoc для создания структуры документации.
    Символ * в начале каждой строки и предшествующие ему пробелы и знаки табуляции игнорируются. Их можно не использовать вообще, но они удобны, когда необходимо форматирование, скажем, в примерах кода.
    /** * Первое предложение - краткое * описание метода. * * Так оформляется пример кода: *
    *
     * if (condition==true) { * x = getWidht(); * y = x.getHeight(); * } * 
    * А так описывается HTML-список: * *
  • Можно использовать наклонный шрифт * курсив, *
  • или жирный жирный. * */ public void calculate (int x, int y) { ... }
    Из этого комментария будет сгенерирован HTML-код, выглядящий примерно так:
    Первое предложение – краткое описание метода.
    Так оформляется пример кода:
    if (condition==true) { x = getWidht(); y = x.getHeight(); }
    А так описывается HTML-список:
    • Можно использовать наклонный шрифт курсив, • или жирный жирный.
    Наконец, javadoc поддерживает специальные теги. Они начинаются с символа @. Подробное описание этих тегов можно найти в документации. Например, можно использовать тег @see, чтобы сослаться на другой класс, поле или метод, или даже на другой Internet-сайт.
    /** * Краткое описание. * * Развернутый комментарий. * * @see java.lang.String * @see java.lang.Math#PI * @see Official * Java site */
    Первая ссылка указывает на класс String (java.lang – название библиотеки, в которой находится этот класс), вторая – на поле PI класса Math (символ # разделяет название класса и его полей или методов), третья ссылается на официальный сайт Java.
    Комментарии разработчика могут быть записаны перед объявлением классов, интерфейсов, полей, методов и конструкторов. Если записать комментарий /** … */ в другой части кода, то ошибки не будет, но он не попадет в документацию, генерируемую javadoc. Кроме того, можно описать пакет (так называются библиотеки, или модули, в Java). Для этого необходимо создать специальный файл package.html, сохранить в нем комментарий и поместить его в каталог пакета. HTML-текст, содержащийся между тегами и , будет помещен в документацию, а первое предложение будет использоваться для краткой характеристики этого пакета.

    Лексемы

    Итак, мы рассмотрели пробелы (в широком смысле этого слова, т.е. все символы, отвечающие за форматирование текста программы) и комментарии, применяемые для ввода пояснений к коду. С точки зрения программиста они применяются для того, чтобы сделать программу более читаемой и понятной для дальнейшего развития.
    С точки зрения компилятора, а точнее его части, отвечающей за лексический разбор, основная роль пробелов и комментариев – служить разделителями между лексемами, причем сами разделители далее отбрасываются и на компилированный код не влияют. Например, все следующие примеры объявления переменной эквивалентны:
    // Используем пробел в качестве разделителя. int x = 3 ;
    // здесь разделителем является перевод строки int x = 3 ;
    // здесь разделяем знаком табуляции int x = 3 ;
    /* * Единственный принципиально необходимый * разделитель между названием типа данных * int и именем переменной x здесь описан * комментарием блочного типа. */ int/**/x=3;
    Конечно, лексемы очень разнообразны, и именно они определяют многие свойства языка. Рассмотрим все их виды более подробно.

    Литералы

    Литералы позволяют задать в программе значения для числовых, символьных и строковых выражений, а также null-литералов. Всего в Java определены следующие виды литералов:
  • целочисленный (integer);
  • дробный (floating-point);
  • булевский (boolean);
  • символьный (character);
  • строковый (string);
  • null-литерал (null-literal).

  • Рассмотрим их по отдельности.

    Логические литералы

    Логические литералы имеют два возможных значения – true и false. Эти два зарезервированных слова не являются ключевыми, но также не могут использоваться в качестве идентификатора.

    Логические операторы

    Логические операторы "и" и "или" (& и |) можно использовать в двух вариантах. Это связано с тем, что, как легко убедиться, для каждого оператора возможны случаи, когда значение первого операнда сразу определяет значение всего логического выражения. Если вторым операндом является значение некоторой функции, то появляется выбор – вызывать ее или нет, причем это решение может сказаться как на скорости, так и на функциональности программы.
    Первый вариант операторов (&, |) всегда вычисляет оба операнда, второй же – ( &&, || ) не будет продолжать вычисления, если значение выражения уже очевидно. Например:
    int x=1; (x>0) | calculate(x) // в таком выражении // произойдет вызов // calculate (x>0) || calculate(x) // а в этом - нет
    Логический оператор отрицания "не" записывается как ! и, конечно, имеет только один вариант использования. Этот оператор меняет булевское значение на противоположное.
    int x=1; x>0 // выражение истинно !(x>0) // выражение ложно
    Оператор с условием ? : состоит из трех частей – условия и двух выражений. Сначала вычисляется условие (булевское выражение), а на основании результата значение всего оператора определяется первым выражением в случае получения истины и вторым – если условие ложно. Например, так можно вычислить модуль числа x:
    x>0 ? x : -x

    Метод destroy

    Перед удалением аплета из памяти вызывается метод destroy, который определен в базовом классе Applet как пустая заглушка. Мастер проектов добавляет в исходный текст класса переопределение метода destroy, которое вы можете при необходимости изменить.
    Методу destroy обычно поручают все необходимые операции, которые следует выполнить перед удалением аплета. Например, если в методе init вы создавали какие-либо потоки, в методе destroy их нужно завершить.

    Метод getAppletInfo

    Назад Вперед
    Базовый класс Applet содержит определение метода getAppletInfo, возвращающее значение null. В нашем классе HelloApplet, который является дочерним по отношению к классу Applet, мы переопределили метод getAppletInfo из базового класса следующим образом:
    public String getAppletInfo() { return "HelloJava Applet"; }
    Теперь метод getAppletInfo возвращает текстовую информацию об аплете в виде объекта класса String.
    Этой информацией могут воспользоваться другие аплеты или сценарии JavaScript, например, для определения возможности взаимодействия с аплетом.
    Назад Вперед

    Метод getAppletInfoМетод getAppletInfo Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Метод getAppletInfo
    printmenus();
    Метод getAppletInfoПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Метод getAppletInfo»


    Метод getAppletInfoSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Метод getAppletInfo»


    Метод getAppletInfoКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Метод getAppletInfo»


    Метод getAppletInfoГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Метод getAppletInfo»


    Метод getAppletInfoОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Метод getAppletInfo»


    Метод getAppletInfoПроекты
    События
    Lab Downloads
    Посмотреть все Метод getAppletInfo»


    Метод getAppletInfo

    Метод init

    Метод init определен в базовом классе Applet, от которого наследуются все аплеты. Определение его таково, что этот метод ровным счетом ничего не делает.
    Когда вызывается метод init и зачем он нужен?
    Метод init вызывается тогда, когда браузер загружает в свое окно документ HTML с оператором , ссылающимся на данный аплет. В этот момент аплет может выполнять инициализацию, или, например, создавать потоки, если он работает в многопоточном режиме.
    Существует контрпара для метода init - метод destroy. О нем мы расскажем ниже.

    Метод paint

    Назад Вперед
    Наиболее интересен для нас метод paint, который выполняет рисование в окне аплета. Вот его исходный текст:
    public void paint(Graphics g) { g.drawString("Hello, Java world!", 20, 20); }
    Если посмотреть определение класса Applet, которое находится в файле JavaWorkshop20\JDK\src\java\applet\Applet.java, то в нем нет метода paint. В каком же классе определен этот метод?

    Метод start

    Метод start вызывается после метода init в момент, когда пользователь начинает просматривать документ HTML с встроенным в него аплетом.
    Вы можете модифицировать текст этого метода, если при каждом посещении пользователем страницы с аплетом необходимо выполнять какую-либо инициализацию.

    Метод stop

    Дополнением к методу start служит метод stop. Он получает управление, когда пользователь покидает страницу с аплетом и загружает в окно браузера другую страницу. Заметим, что метод stop вызывается перед методом destroy.
    Назад Вперед

    Метод stopМетод stop Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Метод stop
    printmenus();
    Метод stopПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Метод stop»


    Метод stopSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Метод stop»


    Метод stopКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Метод stop»


    Метод stopГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Метод stop»


    Метод stopОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Метод stop»


    Метод stopПроекты
    События
    Lab Downloads
    Посмотреть все Метод stop»


    Метод stop

    Методы в классе HelloApplet

    Назад Вперед
    Создавая файл HelloApplet.java, мастер проектов системы Java WorkShop определила в классе HelloApplet несколько методов, заменив таким образом некоторые методы базового класса Applet.

    Null-литерал

    Null-литерал может принимать всего одно значение: null. Это литерал ссылочного типа, причем эта ссылка никуда не ссылается, объект отсутствует. Разумеется, его можно применять к ссылкам любого объектного типа данных. Типы данных подробно рассматриваются в следующей лекции.

    Операторы присваивания и сравнения

    Во-первых, конечно же, различаются оператор присваивания = и оператор сравнения ==.
    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;

    Операторы

    Операторы используются в различных операциях – арифметических, логических, битовых, операциях сравнения и присваивания. Следующие 37 лексем (все состоят только из ASCII-символов) являются операторами языка Java:
    = > < ! ~ ? : == <= >= != && || ++ -- + - * / & | ^ % << >> >>> += -= *= /= &= |= ^= %= <<= >>= >>>=
    Большинство из них вполне очевидны и хорошо известны из других языков программирования, однако некоторые нюансы в работе с операторами в Java все же присутствуют, поэтому в конце лекции приводятся краткие комментарии к ним.

    Первый аплет Java

    Назад Вперед
    В предыдущем разделе мы создавали автономное приложение Java, работающее под управлением виртуальной машины Java. Теперь мы создадим приложение другого типа - аплет.
    Аплет Java тоже выполняется под управлением виртуальной машины Java, но встроенной в браузер. Когда браузер загружает в свое окно документ HTML с аплетом, байт-код аплета начинает свою работу.
    Внешне аплет выглядит как окно заданного размера. Он может рисовать внутри этого окна (и только в нем) произвольные изображения и текст.
    Двоичный файл с интерпретируемым байт-кодом Java располагается на сервере Web. В документе HTML с помощью оператора организуется ссылка на этот двоичный файл.
    Когда пользователь загружает в браузер документ HTML с аплетом, файл аплета переписывается с сервера Web на рабочую станцию пользователя. После этого браузер начинает его выполнение.
    Возможно, вам не понравится такая идея, как запуск чужого аплета на своем компьютере - мало ли чего этот аплет может там сделать. Однако аплеты, в отличие от обычных приложений Java, сильно ограничены в своих правах. Например, они не могут читать локальные файлы и тем более в них писать. Есть также ограничения и на передачу данных через сеть: аплет может обмениваться данными только с тем сервером Web, с которого он был загружен.
    Назад Вперед

    Первый аплет JavaПервый аплет Java Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Первый аплет Java
    printmenus();
    Первый аплет JavaПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Первый аплет Java»


    Первый аплет JavaSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Первый аплет Java»


    Первый аплет JavaКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Первый аплет Java»


    Первый аплет JavaГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Первый аплет 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, скомпилировать и запустить (работа с JDK и стандартными утилитами была рассмотрена в первой лекции). Результатом работы будет, как очевидно:
    Hello, world!

    Пробелы

    Пробелами в данном случае называют все символы, разбивающие текст программы на лексемы. Это как сам символ пробела (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); }
    можно записать и в таком виде:
    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 применяется наиболее гибкий подход. Завершением строки считается:

  • ASCII-символ LF, символ новой строки;

  • ASCII-символ CR, "возврат каретки";
  • символ CR, за которым сразу же следует символ LF.

  • Разбиение на строки важно для корректного разбиения на лексемы (как уже говорилось, завершение строки также служит разделителем между лексемами), для правильной работы со строковыми комментариями (см. следующую тему "Комментарии"), а также для вывода отладочной информации (при выводе ошибок компиляции и времени исполнения указывается, на какой строке исходного кода они возникли). Итак, пробелами в Java считаются:

  • ASCII-символ SP, space, пробел, \u0020, десятичный код 32;

  • ASCII-символ HT, horizontal tab, символ горизонтальной табуляции, \u0009, десятичный код 9;

  • ASCII-символ FF, form feed, символ перевода страницы (был введен для работы с принтером), \u000c, десятичный код 12;
  • завершение строки.


  • Разделители

    Разделители – это специальные символы, которые используются в служебных целях языка. Назначение каждого из них будет рассмотрено по ходу изложения курса. Вот их полный список:
    ( ) [ ] { } ; . ,

    Символьные литералы

    Символьные литералы описывают один символ из набора 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.
    Специальные символы можно использовать в составе как символьных, так и строковых литералов.

    Создание проекта аплета

    Назад Вперед
    Проект аплета создается таким же способом, что и проект автономного приложения Java, однако мастеру проектов необходимо указать другие параметры.
    В первой диалоговой панели мастера проектов следует включить переключатели Applet и No GUI (рис. 1).

    Создание проекта аплета Рис. 1. Первая диалоговая панель мастера проектов
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    Сделав это, нажмите кнопку Next. На экране появится вторая диалоговая панель мастера проектов. Здесь вы должны указать путь к каталогу, куда мастер проектов запишет файлы проекта, а также включить переключатель No (рис. 2).

    Создание проекта аплета Рис. 2. Вторая диалоговая панель мастера проектов
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    В результате мастер проектов создаст исходные тексты аплета, а также добавит новый проект в портфель personal (если этот портфель остался активным в последний раз, когда вы запускали Java WorkShop). Новый проект называется HelloApplet (рис. 3).
    Создание проекта аплета
    Рис. 3. Новый проект появился в активном портфеле personal
    Исходный текст аплета будет создан автоматически и загружен в окно редактирования системы Java WorkShop (рис. 4).

    Создание проекта аплета Рис. 4. Исходный текст аплета загружен в окно редактирования
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    Вы можете оттранслировать полученный исходный текст и запустить аплет на выполнение. Он будет работать под управлением программы просмотра аплетов appletviewer, которая входит в состав Java WorkShop.
    Пока в окне нашего аплета ничего нет (рис. 5), однако скоро мы исправим это положение.
    Создание проекта аплета
    Рис. 5. Окно аплета, созданного автоматически мастером проектов
    Назад Вперед

    Создание проекта аплетаСоздание проекта аплета Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Создание проекта аплета
    printmenus();
    Создание проекта аплетаПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Создание проекта аплета»

    Строковые литералы

    Строковые литералы состоят из набора символов и записываются в двойных кавычках. Длина может быть нулевой или сколь угодно большой. Любой символ может быть представлен специальной последовательностью, начинающейся с \ (см. "Символьные литералы").
    "" // литерал нулевой длины "\"" //литерал, состоящий из одного символа " "Простой текст" //литерал длины 13
    Строковый литерал нельзя разбивать на несколько строк в коде программы. Если требуется текстовое значение, состоящее из нескольких строк, то необходимо воспользоваться специальными символами \n и/или \r. Если же текст просто слишком длинный, чтобы уместиться на одной строке кода, можно использовать оператор конкатенации строк +. Примеры строковых литералов:
    // выражение-константа, составленное из двух // литералов "Длинный текст " + "с переносом" /* * Строковый литерал, содержащий текст * из двух строк: * Hello, world! * Hello! */ "Hello, world!\r\nHello!"
    На строковые литералы распространяются те же правила, что и на символьные в отношении использования символов новой строки \u000a и \u000d.
    Каждый строковый литерал является экземпляром класса String. Это определяет некоторые необычные свойства строковых литералов, которые будут рассмотрены в следующей лекции.

    Виды лексем

    Ниже перечислены все виды лексем в Java:

  • идентификаторы (identifiers);
  • ключевые слова (key words);

  • литералы (literals);
  • разделители (separators);
  • операторы (operators).

  • Рассмотрим их по отдельности.

    Вызов метода paint

    Метод paint вызывается, когда необходимо перерисовать окно аплета. Если вы создавали приложения для операционной системы Windows, то наверняка знакомы с сообщением WM_PAINT, которое поступает в функцию окна приложения при необходимости его перерисовки.
    Перерисовка окна приложения Windows и окна аплета обычно выполняется асинхронно по отношению к работе приложения или аплета. В любой момент времени аплет должен быть готов перерисовать содержимое своего окна.
    Такая техника отличается о той, к которой вы, возможно, привыкли, создавая обычные программы для MS-DOS. Программы MS-DOS сами определяют, когда им нужно рисовать на экране, причем рисование может выполняться из разных мест программы. Аплеты, так же как и приложения Windows, выполняют рисование в своих окнах централизованно. Аплет делает это в методе paint, а приложение Windows - при обработке сообщения WM_PAINT.
    Обратите внимание, что методу paint в качестве параметра передается ссылка на объект Graphics:
    public void paint(Graphics g) { . . . }
    По своему смыслу этот объект напоминает контекст отображения, с которым хорошо знакомы создатели приложений Windows. Контекст отображения - это как бы холст, на котором аплет может рисовать изображение или писать текст. Многочисленные методы класса Graphics позволяют задавать различные параметры холста, такие, например, как цвет или шрифт.
    Наше приложение вызывает метод drawString, который рисует текстовую строку в окне аплета:
    g.drawString("Hello, Java world!", 20, 20);
    Вот прототип этого метода:
    public abstract void drawString(String str, int x, int y);
    Через первый параметр методу drawString передается текстовая строка в виде объекта класса String. Второй и третий параметр определяют, соответственно, координаты точки, в которой начнется рисование строки.
    В какой координатной системе?
    Аплеты используют систему координат, которая соответствует режиму отображения MM_TEXT, знакомому тем, кто создавал приложения Windows. Начало этой системы координат расположено в левом верхнем углу окна аплета, ось X направлена слева направо, а ось Y - сверху вниз (рис. 9).

    В этой лекции были рассмотрены

    В этой лекции были рассмотрены основы лексического анализа программ Java. Для их записи применяется универсальная кодировка Unicode, позволяющая использовать любой язык помимо традиционного английского. Еще раз напомним, что использование Unicode возможно и необходимо в следующих конструкциях:
  • комментарии;

  • идентификаторы;
  • символьные и строковые литералы.

  • Остальные же (пробелы, ключевые слова, числовые, булевские и null-литералы, разделители и операторы) легко записываются с применением лишь ASCII-символов. В то же время любой Unicode-символ также можно задать в виде специальной последовательности ASCII-символов.
    Во время анализа компилятор выделяет из текста программы <пробелы> (были рассмотрены все символы, которые рассматриваются как пробелы) и комментарии, которые полностью удаляются из кода (были рассмотрены все виды комментариев, в частности комментарий разработчика). Пробелы и все виды комментариев служат для разбиения текста программы на лексемы. Были рассмотрены все виды лексем, в том числе все виды литералов.
    В дополнении были рассмотрены особенности применения различных операторов.

    Программирование на Java

    Аплет Draw

    Назад Вперед
    В этом разделе мы приведем исходные тексты аплета Draw, в которых демонстрируется использование различных функций рисования.
    На рис. 11 показано окно этого аплета.
    Аплет Draw
    Рис. 11. Окно аплета Draw
    В верхней части окна мы вывели список вех шрифтов, доступных аплету, а также примеры оформления строки Test string с использованием этих шрифтов.
    В нижней части окна нарисовано несколько геометрических фигур.
    Назад Вперед

    Аплет DrawАплет Draw Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Аплет Draw
    printmenus();
    Аплет DrawПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Аплет Draw»


    Аплет DrawSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Аплет Draw»


    Аплет DrawКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Аплет Draw»


    Аплет DrawГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Аплет Draw»


    Аплет DrawОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Аплет Draw»


    Аплет DrawПроекты
    События
    Lab Downloads
    Посмотреть все Аплет Draw»


    Аплет Draw

    Битовые маски стиля шрифта

  • BOLD

  • public final static int BOLD;
  • ITALIC

  • public final static int ITALIC;
  • PLAIN

  • public final static int PLAIN;

    Булевский тип

    Булевский тип представлен всего одним типом boolean, который может хранить всего два возможных значения – true и false. Величины именно этого типа получаются в результате операций сравнения.
    Над булевскими аргументами можно производить следующие операции:
  • операции сравнения (возвращают булевское значение)

  • ==, !=

  • логические операции (возвращают булевское значение)

  • !

  • &, |, ^

  • &&, ||

  • оператор с условием ? :
  • оператор конкатенации со строкой +

  • Логические операторы && и || обсуждались в предыдущей лекции. В операторе с условием ? : первым аргументом может быть только значение типа boolean. Также допускается, чтобы второй и третий аргументы одновременно имели булевский тип.
    Операция конкатенации со строкой превращает булевскую величину в текст "true" или "false" в зависимости от значения.
    Только булевские выражения допускаются для управления потоком вычислений, например, в качестве критерия условного перехода if.
    Никакое число не может быть интерпретировано как булевское выражение. Если предполагается, что ненулевое значение эквивалентно истине (по правилам языка С), то необходимо записать x!=0. Ссылочные величины можно преобразовывать в boolean выражением ref!=null.

    Целочисленные типы

    Целочисленные типы – это byte, short, int, long, также к ним относят и char. Первые четыре типа имеют длину 1, 2, 4 и 8 байт соответственно, длина char – 2 байта, это непосредственно следует из того, что все символы Java описываются стандартом Unicode. Длины типов приведены только для оценки областей значения. Как уже говорилось, память в Java представляется виртуальной и вычислить, сколько физических ресурсов займет та или иная переменная, так прямолинейно не получится.
    4 основных типа являются знаковыми. char добавлен к целочисленным типам данных, так как с точки зрения JVM символ и его код – понятия взаимооднозначные. Конечно, код символа всегда положительный, поэтому char – единственный беззнаковый тип. Инициализировать его можно как символьным, так и целочисленным литералом. Во всем остальном char – полноценный числовой тип данных, который может участвовать, например, в арифметических действиях, операциях сравнения и т.п. В таблице 4.1 сведены данные по всем разобранным типам:

    Таблица 4.1. Целочисленные типы данных.Название типаДлина (байты)Область значений

    byte
    1
    -128 .. 127

    short
    2
    -32.768 .. 32.767

    int
    4
    -2.147.483.648 .. 2.147.483.647

    long
    8
    -9.223.372.036.854.775.808 .. 9.223.372.036.854.775.807 (примерно 1019)

    char
    2
    '\u0000' .. '\uffff', или 0 .. 65.535

    Обратите внимание, что int вмещает примерно 2 миллиарда, а потому подходит во многих случаях, когда не требуются сверхбольшие числа. Чтобы представить себе размеры типа long, укажем, что именно он используется в Java для отсчета времени. Как и во многих языках, время отсчитывается от 1 января 1970 года в миллисекундах. Так вот, вместимость long позволяет отсчитывать время на протяжении миллионов веков(!), причем как в будущее, так и в прошлое.
    Почему были выделены именно эти два типа, int и long? Дело в том, что целочисленные литералы имеют тип int по умолчанию, или тип long, если стоит буква L или l. Именно поэтому корректным литералом считается только такое число, которое укладывается в 4 или 8 байт, соответственно. Иначе компилятор сочтет это ошибкой. Таким образом, следующие литералы являются корректными:

    1 -2147483648 2147483648L 0L 111111111111111111L

    Над целочисленными аргументами можно производить следующие операции:

  • операции сравнения (возвращают булевское значение)


  • <, <=, >, >=


  • ==, !=


  • числовые операции (возвращают числовое значение)
  • унарные операции + и -
  • арифметические операции +, -, *, /, %
  • операции инкремента и декремента (в префиксной и постфиксной форме): ++ и --
  • операции битового сдвига <<, >>, >>>
  • битовые операции ~, &, |, ^


  • оператор с условием ? :
  • оператор приведения типов
  • оператор конкатенации со строкой +


  • Операторы сравнения вполне очевидны и отдельно мы их рассматривать не будем. Их результат всегда булевского типа (true или false).

    Работа числовых операторов также понятна, к тому же пояснялась в предыдущей лекции. Единственное уточнение можно сделать относительно операторов + и -, которые могут быть как бинарными (иметь два операнда), так и унарными (иметь один операнд). Бинарные операнды являются операторами сложения и вычитания, соответственно. Унарный оператор + возвращает значение, равное аргументу (+x всегда равно x). Унарный оператор -, примененный к значению x, возвращает результат, равный 0-x. Неожиданный эффект имеет место в том случае, если аргумент равен наименьшему возможному значению примитивного типа.

    int x=-2147483648; // наименьшее возможное // значение типа int int y=-x;

    Теперь значение переменной y на самом деле равно не 2147483648, поскольку такое число не укладывается в область значений типа int, а в точности равно значению x! Другими словами, в этом примере выражение -x==x истинно!

    Дело в том, что если при выполнении числовых операций над целыми числами возникает переполнение и результат не может быть сохранен в данном примитивном типе, то Java не создает никаких ошибок. Вместо этого все старшие биты, которые превышают вместимость типа, просто отбрасываются. Это может привести не только к потере точной абсолютной величины результата, но даже к искажению его знака, если на месте знакового бита окажется противоположное значение.


    int x= 300000; print(x*x);

    Результатом такого примера будет:

    -194313216

    Возвращаясь к инвертированию числа -2147483648, мы видим, что математический результат равен в точности +231, или, в двоичном формате, 1000 0000 0000 0000 0000 0000 0000 0000 (единица и 31 ноль). Но тип int рассматривает первую единицу как знаковый бит, и результат получается равным -2147483648.

    Таким образом, явное выписывание в коде литералов, которые слишком велики для используемых типов, приводит к ошибке компиляции (см. лекцию 3). Если же переполнение возникает в результате выполнения операции, "лишние" биты просто отбрасываются.

    Подчеркнем, что выражение типа -5 не является целочисленным литералом. На самом деле оно состоит из литерала 5 и оператора -. Напомним, что некоторые литералы (например, 2147483648) могут встречаться только в сочетании с унарным оператором -.

    Кроме того, числовые операции в Java обладают еще одной особенностью. Хотя целочисленные типы имеют длину 8, 16, 32 и 64 бита, вычисления проводятся только с 32-х и 64-х битной точностью. А это значит, что перед вычислениями может потребоваться преобразовать тип одного или нескольких операндов.

    Если хотя бы один аргумент операции имеет тип long, то все аргументы приводятся к этому типу и результат операции также будет типа long. Вычисление будет произведено с точностью в 64 бита, а более старшие биты, если таковые появляются в результате, отбрасываются.

    Если же аргументов типа long нет, то вычисление производится с точностью в 32 бита, и все аргументы преобразуются в int (это относится к byte, short, char). Результат также имеет тип int. Все биты старше 32-го игнорируются.

    Никакого способа узнать, произошло ли переполнение, нет. Расширим рассмотренный пример:

    int i=300000; print(i*i); // умножение с точностью 32 бита long m=i; print(m*m); // умножение с точностью 64 бита print(1/(m-i)); // попробуем получить разность // значений int и long

    Результатом такого примера будет:

    -194313216 90000000000

    затем мы получим ошибку деления на ноль, поскольку переменные i и m хоть и разных типов, но хранят одинаковое математическое значение и их разность равна нулю. Первое умножение производилось с точностью в 32 бита, более старшие биты были отброшены. Второе – с точностью в 64 бита, ответ не исказился.

    Вопрос приведения типов, и в том числе специальный оператор для такого действия, подробно рассматривается в следующих лекциях. Однако здесь хотелось бы отметить несколько примеров, которые не столь очевидны и могут создать проблемы при написании программ. Во-первых, подчеркнем, что результатом операции с целочисленными аргументами всегда является целое число. А значит, в следующем примере

    double x = 1/2;


    переменной x будет присвоено значение 0, а не 0.5, как можно было бы ожидать. Подробно операции с дробными аргументами рассматриваются ниже, но чтобы получить значение 0.5, достаточно написать 1./2 (теперь первый аргумент дробный и результат не будет округлен).

    Как уже упоминалось, время в Java измеряется в миллисекундах. Попробуем вычислить, сколько миллисекунд содержится в неделе и в месяце:

    print(1000*60*60*24*7); // вычисление для недели print(1000*60*60*24*30); // вычисление для месяца

    Необходимо перемножить количество миллисекунд в одной секунде (1000), секунд – в минуте (60), минут – в часе (60), часов – в дне (24) и дней — в неделе и месяце (7 и 30, соответственно). Получаем:

    604800000 -1702967296

    Очевидно, во втором вычислении произошло переполнение. Достаточно сделать последний аргумент величиной типа long:

    print(1000*60*60*24*30L); // вычисление для месяца

    Получаем правильный результат:

    2592000000

    Подобные вычисления разумно переводить на 64-битную точность не на последней операции, а заранее, чтобы избежать переполнения.

    Понятно, что типы большей длины могут хранить больший спектр значений, а потому Java не позволяет присвоить переменной меньшего типа значение большего типа. Например, такие строки вызовут ошибку компиляции:

    // пример вызовет ошибку компиляции int x=1; byte b=x;

    Хотя для программиста и очевидно, что переменная b должна получить значение 1, что легко укладывается в тип byte, однако компилятор не может вычислять значение переменной x при обработке второй строки, он знает лишь, что ее тип – int.

    А вот менее очевидный пример:

    // пример вызовет ошибку компиляции byte b=1; byte c=b+1;

    И здесь компилятор не сможет успешно завершить работу. При операции сложения значение переменной b будет преобразовано в тип int и таким же будет результат сложения, а значит, его нельзя так просто присвоить переменной типа byte.

    Аналогично:

    // пример вызовет ошибку компиляции int x=2; long y=3; int z=x+y;

    Здесь результат сложения будет уже типа long. Точно так же некорректна такая инициализация:


    // пример вызовет ошибку компиляции byte b=5; byte c=-b;

    Даже унарный оператор "-" проводит вычисления с точностью не меньше 32 бит.

    Хотя во всех случаях инициализация переменных приводилась только для примера, а предметом рассмотрения были числовые операции, укажем корректный способ преобразовать тип числового значения:

    byte b=1; byte c=(byte)-b;

    Итак, все числовые операторы возвращают результат типа int или long. Однако существует два исключения.

    Первое из них – операторы инкрементации и декрементации. Их действие заключается в прибавлении или вычитании единицы из значения переменной, после чего результат сохраняется в этой переменной и значение всей операции равно значению переменной (до или после изменения, в зависимости от того, является оператор префиксным или постфиксным). А значит, и тип значения совпадает с типом переменной. (На самом деле, вычисления все равно производятся с точностью минимум 32 бита, однако при присвоении переменной результата его тип понижается.)

    byte x=5; byte y1=x++; // на момент начала исполнения x равен 5 byte y2=x--; // на момент начала исполнения x равен 6 byte y3=++x; // на момент начала исполнения x равен 5 byte y4=--x; // на момент начала исполнения x равен 6 print(y1); print(y2); print(y3); print(y4);

    В результате получаем:

    5 6 6 5

    Никаких проблем с присвоением результата операторов ++ и -- переменным типа byte. Завершая рассмотрение этих операторов, приведем еще один пример:

    byte x=-128; print(-x);

    byte y=127; print(++y);

    Результатом будет:

    128 -128

    Этот пример иллюстрирует вопросы преобразования типов при вычислениях и случаи переполнения.

    Вторым исключением является оператор с условием ? :. Если второй и третий операнды имеют одинаковый тип, то и результат операции будет такого же типа.

    byte x=2; byte y=3; byte z=(x>y) ? x : y; // верно, x и y одинакового типа byte abs=(x>0) ? x : -x; // неверно!

    Последняя строка неверна, так как третий аргумент содержит числовую операцию, стало быть, его тип int, а значит, и тип всей операции будет int, и присвоение некорректно. Даже если второй аргумент имеет тип byte, а третий – short, значение будет типа int.

    Наконец, рассмотрим оператор конкатенации со строкой. Оператор + может принимать в качестве аргумента строковые величины. Если одним из аргументов является строка, а вторым – целое число, то число будет преобразовано в текст и строки объединятся.


    int x=1; print("x="+x);

    Результатом будет:

    x=1

    Обратите внимание на следующий пример:

    print(1+2+"text"); print("text"+1+2);

    Его результатом будет:

    3text text12

    Отдельно рассмотрим работу с типом char. Значения этого типа могут полноценно участвовать в числовых операциях:

    char c1=10; char c2='A'; // латинская буква A (\u0041, код 65) int i=c1+c2-'B';

    Переменная i получит значение 9.

    Рассмотрим следующий пример:

    char c='A'; print(c); print(c+1); print("c="+c); print('c'+'='+с);

    Его результатом будет:

    A 66 c=A 225

    В первом случае в метод print было передано значение типа char, поэтому отобразился символ. Во втором случае был передан результат сложения, то есть число, и именно число появилось на экране. Далее при сложении со строкой тип char был преобразован в текст в виде символа. Наконец в последней строке произошло сложение трех чисел: 'c' (код 99), '=' (код 61) и переменной c (т.е. код 'A' - 65).

    Для каждого примитивного типа существуют специальные вспомогательные классы-обертки (wrapper classes). Для типов byte, short, int, long, char это Byte, Short, Integer, Long, Character. Эти классы содержат многие полезные методы для работы с целочисленными значениями. Например, преобразование из текста в число. Кроме того, есть класс Math, который хоть и предназначен в основном для работы с дробными числами, но также предоставляет некоторые возможности и для целых.

    В заключение подчеркнем, что единственные операции с целыми числами, при которых Java генерирует ошибки,– это деление на ноль (операторы / и %).

    Документ HTML для аплета Draw

    Документ HTML для аплета Draw не имеет никаких особенностей. Он представлен в листинге 2.
    Листинг 2. Файл draw.tmp.html

    If your browser recognized the applet tag, you would see an applet here.

    Назад Вперед

    Документ HTML для аплета DrawДокумент HTML для аплета Draw Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Документ HTML для аплета Draw
    printmenus();
    Документ HTML для аплета DrawПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Документ HTML для аплета Draw»


    Документ HTML для аплета DrawSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Документ HTML для аплета Draw»


    Документ HTML для аплета DrawКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Документ HTML для аплета Draw»


    Документ HTML для аплета DrawГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Документ HTML для аплета Draw»


    Документ HTML для аплета DrawОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Документ HTML для аплета Draw»


    Документ HTML для аплета DrawПроекты
    События
    Lab Downloads
    Посмотреть все Документ HTML для аплета Draw»


    Документ HTML для аплета Draw

    Дробные типы

    Дробные типы – это float и double. Их длина - 4 и 8 байт, соответственно. Оба типа знаковые. Ниже в таблице сведены их характеристики:

    Таблица 4.2. Дробные типы данных.Название типаДлина (байты)Область значений

    float
    4
    3.40282347e+38f ; 1.40239846e-45f

    double
    8
    1.79769313486231570e+308 ; 4.94065645841246544e-324

    Для целочисленных типов область значений задавалась верхней и нижней границами, весьма близкими по модулю. Для дробных типов добавляется еще одно ограничение – насколько можно приблизиться к нулю, другими словами – каково наименьшее положительное ненулевое значение. Таким образом, нельзя задать литерал заведомо больший, чем позволяет соответствующий тип данных, это приведет к ошибке overflow. И нельзя задать литерал, значение которого по модулю слишком мало для данного типа, компилятор сгенерирует ошибку underflow.
    // пример вызовет ошибку компиляции float f = 1e40f; // значение слишком велико, overflow double d = 1e-350; // значение слишком мало, underflow
    Напомним, что если в конце литерала стоит буква F или f, то литерал рассматривается как значение типа float. По умолчанию дробный литерал имеет тип double, при желании это можно подчеркнуть буквой D или d.
    Над дробными аргументами можно производить следующие операции:
  • операции сравнения (возвращают булевское значение)

  • <, <=, >, >=

  • ==, !=

  • числовые операции (возвращают числовое значение)
  • унарные операции + и -
  • арифметические операции +, -, *, /, %
  • операции инкремента и декремента (в префиксной и постфиксной форме): ++ и --

  • оператор с условием ? :
  • оператор приведения типов
  • оператор конкатенации со строкой +

  • Практически все операторы действуют по тем же принципам, которые предусмотрены для целочисленных операторов (оператор деления с остатком % рассматривался в предыдущей лекции, а операторы ++ и -- также увеличивают или уменьшают значение переменной на единицу). Уточним лишь, что операторы сравнения корректно работают и в случае сравнения целочисленных значений с дробными. Таким образом, в основном необходимо рассмотреть вопросы переполнения и преобразования типов при вычислениях.
    Для дробных вычислений появляется уже два типа переполнения – overflow и underflow. Тем не менее, Java и здесь никак не сообщает о возникновении подобных ситуаций. Нет ни ошибок, ни других способов обнаружить их. Более того, даже деление на ноль не приводит к некорректной ситуации. А значит, дробные вычисления вообще не порождают никаких ошибок.
    Такая свобода связана с наличием специальных значений дробного типа. Они определяются спецификацией IEEE 754 и уже перечислялись в лекции 3:
  • положительная и отрицательная бесконечности (positive/negative infinity);
  • значение "не число", Not-a-Number, сокращенно NaN;
  • положительный и отрицательный нули.


  • Все эти значения представлены как для типа float, так и для double.

    Положительную и отрицательную бесконечности можно получить следующим образом:

    1f/0f // положительная бесконечность, // тип float -1d/0d // отрицательная бесконечность, // тип double

    Также в классах Float и Double определены константы POSITIVE_INFINITY и NEGATIVE_INFINITY. Как видно из примера, такие величины получаются при делении конечных величин на ноль.

    Значение NaN можно получить, например, в результате следующих действий:

    0.0/0.0 // деление ноль на ноль (1.0/0.0)*0.0 // умножение бесконечности на ноль

    Эта величина также представлена константами NaN в классах Float и Double.

    Величины положительный и отрицательный ноль записываются очевидным образом:

    0.0 // дробный литерал со значением // положительного нуля +0.0 // унарная операция +, ее значение - // положительный ноль -0.0 // унарная операция -, ее значение - // отрицательный ноль

    Все дробные значения строго упорядочены. Отрицательная бесконечность меньше любого другого дробного значения, положительная – больше. Значения +0.0 и -0.0 считаются равными, то есть выражение 0.0==-0.0 истинно, а 0.0>-0.0 – ложно. Однако другие операторы различают их, например, выражение 1.0/0.0 дает положительную бесконечность, а 1.0/-0.0 – отрицательную.

    Единственное исключение - значение NaN. Если хотя бы один из аргументов операции сравнения равняется NaN, то результат заведомо будет false (для оператора != соответственно всегда true). Таким образом, единственное значение x, при котором выражение x!=x истинно,– именно NaN.

    Возвращаемся к вопросу переполнения в числовых операциях. Если получаемое значение слишком велико по модулю (overflow), то результатом будет бесконечность соответствующего знака.

    print(1e20f*1e20f); print(-1e200*1e200);

    В результате получаем:

    Infinity -Infinity

    Если результат, напротив, получается слишком мал (underflow), то он просто округляется до нуля. Так же поступают и в том случае, когда количество десятичных знаков превышает допустимое:


    print(1e-40f/1e10f); // underflow для float print(-1e-300/1e100); // underflow для double float f=1e-6f; print(f); f+=0.002f; print(f); f+=3; print(f); f+=4000; print(f);

    Результатом будет:

    0.0 -0.0

    1.0E-6 0.002001 3.002001 4003.002

    Как видно, в последней строке был утрачен 6-й разряд после десятичной точки.

    Другой пример (из спецификации языка Java):

    double d = 1e-305 * Math.PI; print(d); for (int i = 0; i < 4; i++) print(d /= 100000);

    Результатом будет:

    3.141592653589793E-305 3.1415926535898E-310 3.141592653E-315 3.142E-320 0.0

    Таким образом, как и для целочисленных значений, явное выписывание в коде литералов, которые слишком велики (overflow) или слишком малы (underflow) для используемых типов, приводит к ошибке компиляции (см. лекцию 3). Если же переполнение возникает в результате выполнения операции, то возвращается одно из специальных значений.

    Теперь перейдем к преобразованию типов. Если хотя бы один аргумент имеет тип double, то значения всех аргументов приводятся к этому типу и результат операции также будет иметь тип double. Вычисление будет произведено с точностью в 64 бита.

    Если же аргументов типа double нет, а хотя бы один аргумент имеет тип float, то все аргументы приводятся к float, вычисление производится с точностью в 32 бита и результат имеет тип float.

    Эти утверждения верны и в случае, если один из аргументов целочисленный. Если хотя бы один из аргументов имеет значение NaN, то и результатом операции будет NaN.

    Еще раз рассмотрим простой пример:

    print(1/2); print(1/2.);

    Результатом будет:

    0 0.5

    Достаточно одного дробного аргумента, чтобы результат операции также имел дробный тип.

    Более сложный пример:

    int x=3; int y=5; print (x/y); print((double)x/y); print(1.0*x/y);

    Результатом будет:

    0 0.6 0.6

    В первый раз оба аргумента были целыми, поэтому в результате получился ноль. Однако поскольку оба операнда представлены переменными, в этом примере нельзя просто поставить десятичную точку и таким образом перевести вычисления в дробный тип. Необходимо либо преобразовать один из аргументов (второй вывод на экран), либо вставить еще одну фиктивную операцию с дробным аргументом (последняя строка).

    Приведение типов подробно рассматривается в другой лекции, однако обратим здесь внимание на несколько моментов.

    Во-первых, при приведении дробных значений к целым типам дробная часть просто отбрасывается. Например, число 3.84 будет преобразовано в целое 3, а -3.84 превратится в -3. Для математического округления необходимо воспользоваться методом класса Math.round(…).

    Во-вторых, при приведении значений int к типу float и при приведении значений типа long к типу float и double возможны потери точности, несмотря на то, что эти дробные типы вмещают гораздо большие числа, чем соответствующие целые. Рассмотрим следующий пример:

    long l=111111111111L; float f = l; l = (long) f; print(l);

    Результатом будет:

    111111110656

    Тип float не смог сохранить все значащие разряды, хотя преобразование от long к float произошло без специального оператора в отличие от обратного перехода.

    Для каждого примитивного типа существуют специальные вспомогательные классы-обертки (wrapper classes). Для типов float и double это Float и Double. Эти классы содержат многие полезные методы для работы с дробными значениями. Например, преобразование из текста в число.

    Кроме того, класс Math предоставляет большое количество методов для операций над дробными значениями, например, извлечение квадратного корня, возведение в любую степень, тригонометрические и другие. Также в этом классе определены константы PI и основание натурального логарифма E.

    Equals()

    Этот метод имеет один аргумент типа Object и возвращает boolean. Как уже говорилось, equals() служит для сравнения объектов по значению, а не по ссылке. Сравнивается состояние объекта, у которого вызывается этот метод, с передаваемым аргументом.
    Point p1=new Point(2,3); Point p2=new Point(2,3); print(p1.equals(p2));
    Результатом будет true.
    Поскольку сам Object не имеет полей, а значит, и состояния, в этом классе метод equals возвращает результат сравнения по ссылке. Однако при написании нового класса можно переопределить этот метод и описать правильный алгоритм сравнения по значению (что и сделано в большинстве стандартных классов). Соответственно, в класс Point также необходимо добавить переопределенный метод сравнения:
    public boolean equals(Object o) { // Сначала необходимо убедиться, что // переданный объект совместим с типом // Point if (o instanceof Point) { // Типы совместимы, можно провести // преобразование Point p = (Point)o; // Возвращаем результат сравнения // координат return p.x==x && p.y==y; } // Если объект не совместим с Point, // возвращаем false return false; }

    Finalize()

    Данный метод вызывается при уничтожении объекта автоматическим сборщиком мусора (garbage collector). В классе Object он ничего не делает, однако в классе-наследнике позволяет описать все действия, необходимые для корректного удаления объекта, такие как закрытие соединений с БД, сетевых соединений, снятие блокировок на файлы и т.д. В обычном режиме напрямую этот метод вызывать не нужно, он отработает автоматически. Если необходимо, можно обратиться к нему явным образом.
    В методе finalize() нужно описывать только дополнительные действия, связанные с логикой работы программы. Все необходимое для удаления объекта JVM сделает сама.

    Программирование на Java

    Этот метод возвращает объект класса Class, который описывает класс, от которого был порожден этот объект. Класс Class будет рассмотрен ниже. У него есть метод getName(), возвращающий имя класса:
    String s = "abc"; Class cl=s.getClass(); print(cl.getName());
    Результатом будет строка:
    java.lang.String
    В отличие от оператора instanceof, метод getClass() всегда возвращает точно тот класс, от которого был порожден объект.

    HashCode()

    Данный метод возвращает значение int. Цель hashCode() – представить любой объект целым числом. Особенно эффективно это используется в хэш-таблицах (в Java есть стандартная реализация такого хранения данных, она будет рассмотрена позже). Конечно, нельзя потребовать, чтобы различные объекты возвращали различные хэш-коды, но, по крайней мере, необходимо, чтобы объекты, равные по значению (метод equals() возвращает true), возвращали одинаковые хэш-коды.
    В классе Object этот метод реализован на уровне JVM. Сама виртуальная машина генерирует число хеш-кодов, основываясь на расположении объекта в памяти.

    Исходные тексты аплета Draw

    Назад Вперед
    Исходные тексты аплета Draw вы найдете в листинге 1.
    Листинг 1. Файл draw.java
    import java.applet.*; import java.awt.*;
    public class draw extends Applet { Toolkit tk; String szFontList[]; FontMetrics fm; int yStart = 20; int yStep; String parm_TestString;
    public void init() { tk = Toolkit.getDefaultToolkit(); szFontList = tk.getFontList(); parm_TestString = getParameter("TestString"); }
    public String getAppletInfo() { return "Name: draw"; }
    public void paint(Graphics g) { int yDraw; Dimension dimAppWndDimension = getSize();
    g.clearRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);
    g.setColor(Color.yellow); g.fillRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);
    g.setColor(Color.black); g.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);
    fm = g.getFontMetrics(); yStep = fm.getHeight();
    for(int i = 0; i < szFontList.length; i++) { g.setFont(new Font("Helvetica", Font.PLAIN, 12)); g.drawString(szFontList[i], 10, yStart + yStep * i);
    fm = g.getFontMetrics(); yStep = fm.getHeight();
    g.setFont(new Font(szFontList[i], Font.PLAIN, 12)); g.drawString(parm_TestString, 100, yStart + yStep * i); }
    yDraw = yStart + yStep * szFontList.length + yStep;
    Polygon p = new Polygon();
    p.addPoint(70, yDraw); p.addPoint(150, yDraw + 30); p.addPoint(160, yDraw + 80); p.addPoint(190, yDraw + 60); p.addPoint(140, yDraw + 30); p.addPoint(70, yDraw + 39);
    g.drawPolygon(p);
    g.setColor(Color.red); g.drawRect(10, yDraw + 85, 200, 100);
    g.setColor(Color.black); g.drawArc(10, yDraw + 85, 200, 100, -50, 320); }
    public String[][] getParameterInfo() { String[][] info = { { "TestString", "String", "Test string" } }; return info; } }

    Извлечение списка шрифтов

    Процедура извлечения списка доступных шрифтов достаточно проста и выполняется следующим образом:
    Toolkit tk; String szFontList[]; . . . tk = Toolkit.getDefaultToolkit(); szFontList = tk.getFontList();
    Аплет вызывает статический метод getDefaultToolkit из класса Toolkit и затем, пользуясь полученной ссылкой, извлекает список шрифтов, записывая его в массив szFontList.
    Для чего еще можно использовать класс Toolkit?
    Класс Toolkit является абстрактным суперклассом для всех реализаций AWT. Порожденные от него классы используются для привязки различных компонент конкретных реализаций.
    Создавая свои аплеты, вы будете редко прибегать к услугам этого класса. Однако в нем есть несколько полезных методов, прототипы которых мы перечислим ниже:
  • getDefaultToolkit

  • Получение ссылки на Toolkit
    public static Toolkit getDefaultToolkit();
  • getColorModel

  • Определение текущей цветовой модели, выбранной в контекст отображения
    public abstract ColorModel getColorModel();
  • getFontList

  • Получение списка шрифтов, доступных аплету
    public abstract String[] getFontList();
  • getFontMetrics

  • Получение метрик заданного шрифта
    public abstract FontMetrics getFontMetrics(Font font);
  • getImage

  • Получение растрового изображения по имени файла
    public abstract Image getImage(String filename);
  • getImage

  • Получение растрового изображения по адресу URL
    public abstract Image getImage(URL url);
  • getScreenResolution

  • Определение разрешения экрана в точках на дюйм
    public abstract int getScreenResolution();
  • getScreenSize

  • Размеры экрана в пикселах
    public abstract Dimension getScreenSize();
  • prepareImage

  • Подготовка растрового изображения для вывода
    public abstract boolean prepareImage( Image image, int width, int height, ImageObserver observer);
  • sync

  • Синхронизация состояния Toolkit
    public abstract void sync();
    Наиболее интересны, с нашей точки зрения, методы getFontList, getScreenResolution и getScreenSize, с помощью которых аплет может, соответственно, получить список шрифтов, определить разрешение и размер экрана. Последние два параметра позволяют сформировать содержимое окна аплета оптимальным образом исходя из объема информации, который может в нем разместиться.

    Класс Class

    Наконец, последний класс, который будет рассмотрен в этой лекции.
    Класс Class является метаклассом для всех классов Java. Когда JVM загружает файл .class, который описывает некоторый тип, в памяти создается объект класса Class, который будет хранить это описание.
    Например, если в программе есть строка
    Point p=new Point(1,2);
    то это означает, что в системе созданы следующие объекты:
  • объект типа Point, описывающий точку (1,2);
  • объект класса Class, описывающий класс Point;
  • объект класса Class, описывающий класс Object. Поскольку класс Point наследуется от Object, его описание также необходимо;
  • объект класса Class, описывающий класс Class. Это обычный Java-класс, который должен быть загружен по общим правилам.

  • Одно из применений класса Class уже было рассмотрено – использование метода getClass() класса Object. Если продолжить последний пример с точкой:
    Class cl=p.getClass(); // это объект №2 из списка Class cl2=cl.getClass(); // это объект №4 из списка Class cl3=cl2.getClass(); // опять объект №4
    Выражение cl2==cl3 верно.
    Другое применение класса Class также приводилось в примере применения технологии reflection.
    Кроме прямого использования метакласса для хранения в памяти описания классов, Java использует эти объекты и для других целей, которые будут рассмотрены ниже (статические переменные, синхронизация статических методов и т.д.).

    Класс Font

    Приведем краткое перечисление полей, конструкторов и методов этого класса.

    Класс Object

    В Java множественное наследование отсутствует. Каждый класс может иметь только одного родителя. Таким образом, мы можем проследить цепочку наследования от любого класса, поднимаясь все выше. Существует класс, на котором такая цепочка всегда заканчивается, это класс Object. Именно от него наследуются все классы, в объявлении которых явно не указан другой родительский класс. А значит, любой класс напрямую, или через своих родителей, является наследником Object. Отсюда следует, что методы этого класса есть у любого объекта (поля в Object отсутствуют), а потому они представляют особенный интерес.
    Рассмотрим основные из них.

    Класс String

    Как уже указывалось, класс String занимает в Java особое положение. Экземпляры только этого класса можно создавать без использования ключевого слова new. Каждый строковый литерал порождает экземпляр String, и это единственный литерал (кроме null), имеющий объектный тип.
    Затем значение любого типа может быть приведено к строке с помощью оператора конкатенации строк, который был рассмотрен для каждого типа, как примитивного, так и объектного.
    Еще одним важным свойством данного класса является неизменяемость. Это означает, что, породив объект, содержащий некое значение-строку, мы уже не можем изменить данное значение – необходимо создать новый объект.
    String s="a"; s="b";
    Во второй строке переменная сменила свое значение, но только создав новый объект класса String.
    Поскольку каждый строковый литерал порождает новый объект, что есть очень ресурсоемкая операция в Java, зачастую компилятор стремится оптимизировать эту работу.
    Во-первых, если используется несколько литералов с одинаковым значением, для них будет создан один и тот же объект.
    String s1 = "abc"; String s2 = "abc"; String s3 = "a"+"bc"; print(s1==s2); print(s1==s3);
    Результатом будет:
    true true
    То есть в случае, когда строка конструируется из констант, известных уже на момент компиляции, оптимизатор также подставляет один и тот же объект.
    Если же строка создается выражением, которое может быть вычислено только во время исполнения программы, то оно будет порождать новый объект:
    String s1="abc"; String s2="ab"; print(s1==(s2+"c"));
    Результатом будет false, так как компилятор не может предсказать результат сложения значения переменной с константой.
    В классе String определен метод intern(), который возвращает один и тот же объект-строку для всех экземпляров, равных по значению. То есть если для ссылок s1 и s2 верно выражение s1.equals(s2), то верно и s1.intern()==s2.intern().
    Разумеется, в классе переопределены методы equals() и hashCode(). Метод toString() также переопределен и возвращает он сам объект-строку, то есть для любой ссылки s типа String, не равной null, верно выражение s==s.toString().

    Как уже указывалось, класс String занимает в Java особое положение. Экземпляры только этого класса можно создавать без использования ключевого слова new. Каждый строковый литерал порождает экземпляр String, и это единственный литерал (кроме null), имеющий объектный тип.
    Затем значение любого типа может быть приведено к строке с помощью оператора конкатенации строк, который был рассмотрен для каждого типа, как примитивного, так и объектного.
    Еще одним важным свойством данного класса является неизменяемость. Это означает, что, породив объект, содержащий некое значение-строку, мы уже не можем изменить данное значение – необходимо создать новый объект.
    String s="a"; s="b";
    Во второй строке переменная сменила свое значение, но только создав новый объект класса String.
    Поскольку каждый строковый литерал порождает новый объект, что есть очень ресурсоемкая операция в Java, зачастую компилятор стремится оптимизировать эту работу.
    Во-первых, если используется несколько литералов с одинаковым значением, для них будет создан один и тот же объект.
    String s1 = "abc"; String s2 = "abc"; String s3 = "a"+"bc"; print(s1==s2); print(s1==s3);
    Результатом будет:
    true true
    То есть в случае, когда строка конструируется из констант, известных уже на момент компиляции, оптимизатор также подставляет один и тот же объект.
    Если же строка создается выражением, которое может быть вычислено только во время исполнения программы, то оно будет порождать новый объект:
    String s1="abc"; String s2="ab"; print(s1==(s2+"c"));
    Результатом будет false, так как компилятор не может предсказать результат сложения значения переменной с константой.
    В классе String определен метод intern(), который возвращает один и тот же объект-строку для всех экземпляров, равных по значению. То есть если для ссылок s1 и s2 верно выражение s1.equals(s2), то верно и s1.intern()==s2.intern().
    Разумеется, в классе переопределены методы equals() и hashCode(). Метод toString() также переопределен и возвращает он сам объект-строку, то есть для любой ссылки s типа String, не равной null, верно выражение s==s.toString().

    Контекст отображения

    Назад Вперед
    Проще всего представить себе контекст отображения как полотно, на котором рисует художник. Точно так же как художник может выбирать для рисования различные инструменты, программист, создающий аплет Java, может выбирать различные методы класса Graphics и задавать различные атрибуты контекста отображения.

    Копирование содержимого прямоугольной области

    Метод copyArea позволяет скопировать содержимое любой прямоугольной области окна аплета:
    public abstract void copyArea( int x, int y, int width, int height, int dx, int dy);
    Параметры x, y, width и height задают координаты копируемой прямоугольной области. Область копируется в другую прямоугольную область такого же размера, причем параметры dx и dy определяют координаты последней.
    Назад Вперед

    Копирование содержимого прямоугольной областиКопирование содержимого прямоугольной области Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Копирование содержимого прямоугольной области
    printmenus();
    Копирование содержимого прямоугольной областиПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Копирование содержимого прямоугольной области»


    Копирование содержимого прямоугольной областиSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Копирование содержимого прямоугольной области»


    Копирование содержимого прямоугольной областиКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Копирование содержимого прямоугольной области»


    Копирование содержимого прямоугольной областиГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Копирование содержимого прямоугольной области»


    Копирование содержимого прямоугольной областиОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Копирование содержимого прямоугольной области»


    Копирование содержимого прямоугольной областиПроекты
    События
    Lab Downloads
    Посмотреть все Копирование содержимого прямоугольной области»


    Копирование содержимого прямоугольной области

    Линии

    Для того чтобы нарисовать прямую тонкую сплошную линию, вы можете воспользоваться методом drawLine, прототип которого приведен ниже:
    public abstract void drawLine(int x1, int y1,int x2, int y2);
    Концы линии имеют координаты (x1, y1) и (x2, y2), как это показано нарис. 1.
    Линии
    Рис. 1. Рисование прямой линии
    К сожалению, в контексте отображения не предусмотрены никакие атрибуты, позволяющие нарисовать пунктирную линию или линию увеличенной толщины.

    Метод init

    При инициализации аплета метод init извлекает список доступных шрифтов и принимает значение параметра TestString, передаваемое аплету в документе HTML.

    Метод paint

    Первым делом метод paint определяет размеры окна аплета, вызывая для этого метод getSize:
    Dimension dimAppWndDimension = getSize();
    Метод getSize возвращает ссылку на объект класса Dimension, хранящий высоту и ширину объекта:
  • height

  • Высота
    public int height;
  • width

  • Ширина
    public int width;
    В классе Dimension предусмотрено три конструктора и один метод:
    public Dimension(); public Dimension(Dimension d); public Dimension(int width, int height);
  • toString

  • Получение строки, представляющей класс
    public String toString();
    После определения размеров окна аплета метод paint стирает содержимое всего окна:
    g.clearRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);
    Далее в контексте отображения устанавливается желтый цвет:
    g.setColor(Color.yellow);
    Этим цветом заполняется внутренняя область окна аплета, для чего применяется метод fillRect:
    g.fillRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);
    Затем метод paint устанавливает в контексте отображения черный цвет и рисует тонкую черную рамку вокруг окна аплета:
    g.setColor(Color.black); g.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);
    На следующем этапе мы получаем метрики текущего шрифта, выбранного в контекст отображения:
    fm = g.getFontMetrics();
    Пользуясь этими метриками, мы определяем высоту символов текущего шрифта и записываем ее в поле yStep:
    yStep = fm.getHeight();
    После этого метод paint запускает цикл по всем шрифтам, установленным в системе:
    for(int i = 0; i < szFontList.length; i++) { . . . }
    Количество доступных шрифтов равно размеру массива szFontList, которое вычисляется как szFontList.length.
    Метод paint выписывает в окне аплета название каждого шрифта, устанавливая для этого шрифт Helvetica размером 12 пикселов:
    g.setFont(new Font("Helvetica", Font.PLAIN, 12)); g.drawString(szFontList[i], 10, yStart + yStep * i);
    Смещение каждой новой строки с названием шрифта вычисляется исходя из высоты символов установленного шрифта:

    fm = g.getFontMetrics(); yStep = fm.getHeight();

    После названия шрифта метод paint рисует в окне аплета текстовую строку parm_TestString, полученную через параметр с именем "TestString":

    g.setFont(new Font(szFontList[i], Font.PLAIN, 12)); g.drawString(parm_TestString, 100, yStart + yStep * i);

    Перед тем как перейти к рисованию геометрических фигур, метод paint запоминает в поле yDraw координату последней строки названия шрифта, сделав отступ высотой yStep :

    int yDraw; yDraw = yStart + yStep * szFontList.length + yStep;

    Первая фигура, которую рисует наш аплет, это многоугольник.

    Мы создаем многоугольник как объект класса Polygon:

    Polygon p = new Polygon();

    В этот объект при помощи метода addPoint добавляется несколько точек:

    p.addPoint(70, yDraw); p.addPoint(150, yDraw + 30); p.addPoint(160, yDraw + 80); p.addPoint(190, yDraw + 60); p.addPoint(140, yDraw + 30); p.addPoint(70, yDraw + 39);

    После добавления всех точек метод paint рисует многоугольник, вызывая для этого метод drawPolygon:

    g.drawPolygon(p);

    Затем мы устанавливаем в контексте отображения красный цвет и рисуем прямоугольник:

    g.setColor(Color.red); g.drawRect(10, yDraw + 85, 200, 100);

    Затем метод paint вписывает в этот прямоугольник сегмент окружности:

    g.setColor(Color.black); g.drawArc(10, yDraw + 85, 200, 100, -50, 320);

    Методы класса Graphics

    В качестве базового для класса Graphics (полное название класса java.awt.Graphics) выступает класс java.lang.Object.
    Прежде всего мы приведем прототипы конструктора этого класса и его методов с краткими комментариями. Полное описание вы сможете найти в электронной документации, которая входит в комплект Java WorkShop.
    Далее мы рассмотрим назначение основных методов, сгруппировав их по выполняемым функциям.

    Методы

  • clearRect

  • Стирание содержимого прямоугольной области
    public abstract void clearRect(int x, int y, int width, int height);
  • clipRect

  • Задание области ограничения вывода
    public abstract void clipRect(int x, int y, int width, int height);
  • copyArea

  • Копирование содержимого прямоугольной области
    public abstract void copyArea(int x, int y, int width, int height, int dx, int dy);
  • create

  • Создание контекста отображения
    public abstract Graphics create();
    public Graphics create(int x, int y, int width, int height);
  • dispose

  • Удаление контекста отображения
    public abstract void dispose();
  • draw3DRect

  • Рисование прямоугольной области с трехмерным выделением
    public void draw3DRect(int x, int y, int width, int height, boolean raised);
  • drawArc

  • Рисование сегмента
    public abstract void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle);
    Рисование сегмента
  • drawBytes

  • Рисование текста из массива байт
    public void drawBytes(byte data[], int offset, int length, int x, int y);
  • drawChars

  • Рисование текста из массива символов
    public void drawChars(char data[], int offset, int length, int x, int y);
  • drawImage

  • Рисование растрового изображения
    public abstract boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer);
    public abstract boolean drawImage(Image img, int x, int y, ImageObserver observer);
    public abstract boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer);
    public abstract boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer);
  • drawLine

  • Рисование линии
    public abstract void drawLine(int x1, int y1, int x2, int y2);
  • drawOval

  • Рисование овала
    public abstract void drawOval(int x, int y, int width, int height);
  • drawPolygon

  • Рисование многоугольника
    public abstract void drawPolygon( int xPoints[], int yPoints[], int nPoints);
    public void drawPolygon(Polygon p);
  • drawRect

  • Рисование прямоугольника
    public void drawRect(int x, int y, int width, int height);

  • equals

  • Сравнение шрифтов
    public boolean equals(Object obj);
  • getFamily

  • Определение названия семейства шрифтов
    public String getFamily();
  • getFont

  • Получение шрифта по его характеристикам
    public static Font getFont(String nm); public static Font getFont(String nm, Font font);
  • getName

  • Определение названия шрифта
    public String getName();
  • getSize

  • Определение размера шрифта
    public int getSize();
  • getStyle

  • Определение стиля шрифта
    public int getStyle();
  • hashCode

  • Получение хэш-кода шрифта
    public int hashCode();
  • isBold

  • Определение жирности шрифта
    public boolean isBold();
  • isItalic

  • Проверка, является ли шрифт наклонным
    public boolean isItalic();
  • isPlain

  • Проверка, есть ли шрифтовое выделение
    public boolean isPlain();
  • toString

  • Получение текстовой строки для объекта
    public String toString();
    Создавая шрифт конструктором Font, вы должны указать имя, стиль и размер шрифта.
    В качестве имени можно указать, например, такие строки как Helvetica или Courier. Учтите, что в системе удаленного пользователя, загрузившего ваш аплет, может не найтись шрифта с указанным вами именем. В этом случае браузер заменит его на наиболее подходящий (с его точки зрения).
    Стиль шрифта задается масками BOLD, ITALIC и PLAIN, которые можно комбинировать при помощи логической операции "ИЛИ":

    Маска Описание
    BOLD Утолщенный шрифт
    ITALIC Наклонный шрифт
    PLAIN Шрифтовое выделение не используется

    Что же касается размера шрифта, то он указывается в пикселах.
    Назад Вперед

    МетодыМетоды Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Методы
    printmenus();
    МетодыПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Методы»


    МетодыSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Методы»


    МетодыКаталог решений
    Истории успеха


  • addPoint

  • Добавление вершины
    public void addPoint(int x, int y);
  • getBoundingBox

  • Получение координат охватывающего прямоугольника
    public Rectangle getBoundingBox();
  • inside

  • Проверка, находится ли точка внутри многоугольника
    public boolean inside(int x, int y);
    Ниже мы показали фрагмент кода, в котором создается многоугольник, а затем в него добавляется несколько точек. Многоугольник рисуется методом drawPolygon:
    Polygon p = new Polygon(); p.addPoint(270, 239); p.addPoint(350, 230); p.addPoint(360, 180); p.addPoint(390, 160); p.addPoint(340, 130); p.addPoint(270, 239); g.drawPolygon(p);
    Если вам нужно нарисовать заполненный многоугольник (рис. 7), то для этого вы можете воспользоваться методами, приведенными ниже:
    public abstract void fillPolygon( int xPoints[], int yPoints[], int nPoints);
    public void fillPolygon(Polygon p);
    Первый из этих методов рисует многоугольник, координаты вершин которого заданы в массивах, второй - получая объект класса Polygon в качестве параметра.
    Методы
    Рис. 7. Многоугольник, нарисованный методом fillPolygon

    Многоугольники

    Для рисования многоугольников в классе Graphics предусмотрено четыре метода, два из которых рисуют незаполненные многоугольники, а два - заполненные.
    Первый метод рисует незаполненный многоугольник, заданный массивами координат по осям X и Y:
    public abstract void drawPolygon( int xPoints[], int yPoints[], int nPoints);
    Через параметры xPoints и yPoints передаются, соответственно, ссылки на массивы координат по оис X и Y. Параметр nPoints задает количество точек в массивах.
    На рис. 6 показан многоугольник, нарисованный методом drawPolygon.
    Многоугольники
    Рис. 6. Многоугольник, нарисованный методом drawPolygon
    В этом многоугольнике шесть вершин с координатами от (x0,y0) до (x5, y5), причем для того чтобы он стал замкнутым, координаты первой и последней вершины совпадают.
    Второй метод также рисует незаполненный многоугольник, однако в качестве параметра методу передается ссылка на объект Polygon:
    public void drawPolygon(Polygon p);
    Класс Polygon достаточно прост. Приведем описание его полей, конструкторов и методов:

    Объекты и правила работы с ними

    Объект (object) – это экземпляр некоторого класса, или экземпляр массива. Массивы будут подробно рассматриваться в соответствующей лекции. Класс – это описание объектов одинаковой структуры, и если в программе такой класс используется, то описание присутствует в единственном экземпляре. Объектов этого класса может не быть вовсе, а может быть создано сколь угодно много.
    Объекты всегда создаются с использованием ключевого слова new, причем одно слово new порождает строго один объект (или вовсе ни одного, если происходит ошибка). После ключевого слова указывается имя класса, от которого мы собираемся породить объект. Создание объекта всегда происходит через вызов одного из конструкторов класса (их может быть несколько), поэтому в заключение ставятся скобки, в которых перечислены значения аргументов, передаваемых выбранному конструктору. В приведенных выше примерах, когда создавались объекты типа Point, выражение new Point (3,5) означало обращение к конструктору класса Point, у которого есть два аргумента типа int. Кстати, обязательное объявление такого конструктора в упрощенном объявлении класса отсутствовало. Объявление классов рассматривается в следующих лекциях, однако приведем правильное определение Point:
    class Point { int x, y;
    /** * Конструктор принимает 2 аргумента, * которыми инициализирует поля объекта. */ Point (int newx, int newy){ x=newx; y=newy; } }
    Если конструктор отработал успешно, то выражение new возвращает ссылку на созданный объект. Эту ссылку можно сохранить в переменной, передать в качестве аргумента в какой-либо метод или использовать другим способом. JVM всегда занимается подсчетом хранимых ссылок на каждый объект. Как только обнаруживается, что ссылок больше нет, такой объект предназначается для уничтожения сборщиком мусора (garbage collector). Восстановить ссылку на такой "потерянный" объект невозможно.
    Point p=new Point(1,2); // Создали объект, получили на него ссылку Point p1=p; // теперь есть 2 ссылки на точку (1,2) p=new Point(3,4); // осталась одна ссылка на точку (1,2) p1=null;

    Ссылок на объект-точку (1,2) больше нет, доступ к нему утерян и он вскоре будет уничтожен сборщиком мусора.

    Любой объект порождается только с применением ключевого слова new. Единственное исключение – экземпляры класса String. Записывая любой строковый литерал, мы автоматически порождаем объект этого класса. Оператор конкатенации +, результатом которого является строка, также неявно порождает объекты без использования ключевого слова new.

    Рассмотрим пример:

    "abc"+"def"

    При выполнении этого выражения будет создано три объекта класса String. Два объекта порождаются строковыми литералами, третий будет представлять результат конкатенации.

    Операция создания объекта – одна из самых ресурсоемких в Java. Поэтому следует избегать ненужных порождений. Поскольку при работе со строками их может создаваться довольно много, компилятор, как правило, пытается оптимизировать такие выражения. В рассмотренном примере, поскольку все операнды являются константами времени компиляции, компилятор сам осуществит конкатенацию и вставит в код уже результат, сократив таким образом количество создаваемых объектов до одного.

    Кроме того, в версии Java 1.1 была введена технология reflection, которая позволяет обращаться к классам, методам и полям, используя лишь их имя в текстовом виде. С ее помощью также можно создать объект без ключевого слова new, однако эта технология довольно специфична, применяется в редких случаях, а кроме того, довольно проста и потому в данном курсе не рассматривается. Все же приведем пример ее применения:

    Point p = null; try { // в следующей строке, используя лишь // текстовое имя класса Point, порождается // объект без применения ключевого слова new p=(Point)Class.forName("Point").newInstance();

    } catch (Exception e) { // обработка ошибок System.out.println(e); }

    Объект всегда "помнит", от какого класса он был порожден. С другой стороны, как уже указывалось, можно ссылаться на объект, используя ссылку другого типа. Приведем пример, который будем еще много раз использовать. Сначала опишем два класса, Parent и его наследник Child:


    // Объявляем класс Parent class Parent { }

    // Объявляем класс Child и наследуем // его от класса Parent class Child extends Parent { }

    Пока нам не нужно определять какие-либо поля или методы. Далее объявим переменную одного типа и проинициализируем ее значением другого типа:

    Parent p = new Child();

    Теперь переменная типа Parent указывает на объект, порожденный от класса Child.

    Над ссылочными значениями можно производить следующие операции:

  • обращение к полям и методам объекта
  • оператор instanceof (возвращает булевское значение)
  • операции сравнения == и != (возвращают булевское значение)
  • оператор приведения типов
  • оператор с условием ? :
  • оператор конкатенации со строкой +


  • Обращение к полям и методам объекта можно назвать основной операцией над ссылочными величинами. Осуществляется она с помощью символа . (точка). Примеры ее применения будут приводиться.

    Используя оператор instanceof, можно узнать, от какого класса произошел объект. Этот оператор имеет два аргумента. Слева указывается ссылка на объект, а справа – имя типа, на совместимость с которым проверяется объект. Например:

    Parent p = new Child(); // проверяем переменную p типа Parent // на совместимость с типом Child print(p instanceof Child);

    Результатом будет true. Таким образом, оператор instanceof опирается не на тип ссылки, а на свойства объекта, на который она ссылается. Но этот оператор возвращает истинное значение не только для того типа, от которого был порожден объект. Добавим к уже объявленным классам еще один:

    // Объявляем новый класс и наследуем // его от класса Child class ChildOfChild extends Child { }

    Теперь заведем переменную нового типа:

    Parent p = new ChildOfChild(); print(p instanceof Child);

    В первой строке объявляется переменная типа Parent, которая инициализируется ссылкой на объект, порожденный от ChildOfChild. Во второй строке оператор instanceof анализирует совместимость ссылки типа Parent с классом Child, причем задействованный объект не порожден ни от первого, ни от второго класса. Тем не менее, оператор вернет true, поскольку класс, от которого этот объект порожден, наследуется от Child.

    Добавим еще один класс:


    class Child2 extends Parent { }

    И снова объявим переменную типа Parent:

    Parent p=new Child(); print(p instanceof Child); print(p instanceof Child2);

    Переменная p имеет тип Parent, а значит, может ссылаться на объекты типа Child или Child2. Оператор instanceof помогает разобраться в ситуации:

    true false

    Для ссылки, равной null, оператор instanceof всегда вернет значение false.

    С изучением свойств объектной модели Java мы будем возвращаться к алгоритму работы оператора instanceof.

    Операторы сравнения == и != проверяют равенство (или неравенство) объектных величин именно по ссылке. Однако часто требуется альтернативное сравнение – по значению. Сравнение по значению имеет дело с понятием состояние объекта. Сам смысл этого выражения рассматривается в ООП, что же касается реализации в Java, то состояние объекта хранится в его полях. При сравнении по ссылке ни тип объекта, ни значения его полей не учитываются, true возвращается только в том случае, если обе ссылки указывают на один и тот же объект.

    Point p1=new Point(2,3); Point p2=p1; Point p3=new Point(2,3); print(p1==p2); print(p1==p3);

    Результатом будет:

    true false

    Первое сравнение оказалось истинным, так как переменная p2 ссылается на тот же объект, что и p1. Второе же сравнение ложно, несмотря на то, что переменная p3 ссылается на объект-точку с точно такими же координатами. Однако это другой объект, который был порожден другим выражением new.

    Если один из аргументов оператора = равен null, а другой – нет, то значение такого выражения будет false. Если же оба операнда null, то результат будет true.

    Для корректного сравнения по значению существует специальный метод equals, который будет рассмотрен позже. Например, строки надо сравнивать следующим образом:

    String s = "abc"; s=s+1; print(s.equals("abc1"));

    Операция с условием ? : работает как обычно и может принимать второй и третий аргументы, если они оба одновременно ссылочного типа. Результат такого оператора также будет иметь объектный тип.

    Как и простые типы, ссылочные величины можно складывать со строкой. Если ссылка равна null, то к строке добавляется текст "null". Если же ссылка указывает на объект, то у него вызывается специальный метод (он будет рассмотрен ниже, его имя toString()) и текст, который он вернет, будет добавлен к строке.

    Определение атрибутов контекста отображения

    Назад Вперед
    Ряд методов класса Graphics позволяет определить различные атрибуты контекста отображения, например, цвет, выбранный в контекст отображения или метрики текущего шрифта, которым выполняется рисование текста.
    Рассмотрим методы, позволяющие определить атрибуты контекста отображения.

    Определение цвета, выбранного в контекст отображения

    Метод getColor возвращает ссылку на объект класса Color, представляющий текущий цвет, выбранный в контекст отображения:
    public abstract Color getColor();

    Определение границ области ограничения вывода

    С помощью метода clipRect, о котором мы расскажем чуть позже, вы можете определить в окне аплета область ограничения вывода прямоугольной формы. Вне этой области рисование графических изображений и текста не выполняется.
    Метод getClipRect позволяет вам определить координаты текущей области ограничения, заданной в контексте отображения:
    public abstract Rectangle getClipRect();
    Метод возвращает ссылку на объект класса Rectangle, который, в частности, имеет поля класса с именами x, y, height и width. В этих полях находится, соответственно, координаты верхнего левого угла, высота и ширина прямоугольной области.

    Определение метрик текущего шрифта

    Несмотря на то что вы можете заказать шрифт с заданным именем и размером, не следует надеяться, что навигатор выделит вам именно такой шрифт, какой вы попросите. Для правильного размещения текста и других изображений в окне аплета вам необходимо знать метрики реального шрифта, выбранного навигатором в контекст отображения.
    Метрики текущего шрифта в контексте отображения вы можете узнать при помощи метода getFontMetrics, прототип которого приведен ниже:
    public FontMetrics getFontMetrics();
    Метод getFontMetrics возвращает ссылку на объект класса FontMetrics. Ниже мы привели список наиболее важных методов этого класса, предназначенных для получения отдельных параметров шрифта:

    Метод Описание
    public Font getFont(); Определение шрифта, который описывается данной метрикой
    public int bytesWidth(byte data[], int off, int len); Метод возвращает ширину строки символов, расположенных в массиве байт data. Параметры off и len задают, соответственно, смещение начала строки в массиве и ее длину
    public int charsWidth(char data[], int off, int len); Метод возвращает ширину строки символов, расположенных в массиве символов data. Параметры off и len задают, соответственно, смещение начала строки в массиве и ее длину
    public int charWidth(char ch); Метод возвращает ширину заданного символа
    public int charWidth(int ch); Метод возвращает ширину заданной строки символов
    public int getAscent(); Определение расстояния от базовой линии до верхней выступающей части символов
    public int getDescent(); Определение расстояния от базовой линии до нижней выступающей части символов
    public int getLeading(); Расстояние между строками текста
    public int getHeight(); Определение полной высоты символов, выполняется по формуле:
    getLeading() + getAscent() + getDescent()
    public int getMaxAdvance(); Максимальная ширина символов в шрифте
    public int getMaxAscent(); Максимальное расстояние от базовой линии до верхней выступающей части символов для символов данного шрифта
    public int getMaxDescent(); Максимальное расстояние от базовой линии до нижней выступающей части символов для символов данного шрифта
    public int[] getWidths(); Массив, в котором хранятся значения ширины первых 256 символов в шрифте
    public int stringWidth(String str); Ширина строки, передаваемой методу в качестве параметра
    public String toString(); Текстовая строка, представляющая данную метрику шрифта

    Обратите внимание на метод stringWidth, позволяющий определить ширину текстовой строки. Заметим, что без этого метода определение ширины текстовой строки было бы непростой задачей, особенно если шрифт имеет переменную ширину символов.
    Для определения полной высоты строки символов вы можете воспользоваться методом getHeight.

    Определение метрик заданного шрифта

    Метод getFontMetrics с параметром типа Font позволяет определить метрики любого шрифта, передаваемого ему в качестве параметра:
    public abstract FontMetrics getFontMetrics(Font f);
    В отличие от нее метод getFontMetrics без параметров возвращает метрики текущего шрифта, выбранного в контекст отображения.
    Назад Вперед

    Определение метрик заданного шрифтаОпределение метрик заданного шрифта Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Определение метрик заданного шрифта
    printmenus();
    Определение метрик заданного шрифтаПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Определение метрик заданного шрифта»


    Определение метрик заданного шрифтаSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Определение метрик заданного шрифта»


    Определение метрик заданного шрифтаКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Определение метрик заданного шрифта»


    Определение метрик заданного шрифтаГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Определение метрик заданного шрифта»


    Определение метрик заданного шрифтаОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Определение метрик заданного шрифта»


    Определение метрик заданного шрифтаПроекты
    События
    Lab Downloads
    Посмотреть все Определение метрик заданного шрифта»


    Определение метрик заданного шрифта

    Определение шрифта, выбранного в контекст отображения

    С помощью метода getFont, возвращающего ссылку на объект класса Font, вы можете определить текущий шрифт, выбранный в контекст отображения:
    public abstract Font getFont();

    Овалы и круги

    Для рисования окружностей и овалов вы можете воспользоваться методом drawOval:
    public abstract void drawOval( int x, int y,
    int width, int height);
    Параметры этого методы задают координаты и размеры прямоугольника, в который вписывается рисуемый овал (рис. 8).
    Овалы и круги
    Рис. 8. Рисование овала
    Метод fillOval предназначен для рисования заполненного овала (рис. 9). Назначение его параметров аналогично назначению параметров метода drawOval:
    public abstract void fillOval( int x, int y, int width, int height);
    Овалы и круги
    Рис. 9. Рисование заполненного овала

    Переменные

    Переменные используются в программе для хранения данных. Любая переменная имеет три базовых характеристики:
  • имя;
  • тип;
  • значение.

  • Имя уникально идентифицирует переменную и позволяет обращаться к ней в программе. Тип описывает, какие величины может хранить переменная. Значение – текущая величина, хранящаяся в переменной на данный момент.
    Работа с переменной всегда начинается с ее объявления (declaration). Конечно, оно должно включать в себя имя объявляемой переменной. Как было сказано, в Java любая переменная имеет строгий тип, который также задается при объявлении и никогда не меняется. Значение может быть указано сразу (это называется инициализацией), а в большинстве случаев задание начальной величины можно и отложить.
    Некоторые примеры объявления переменных примитивного типа int с инициализаторами и без таковых:
    int a; int b = 0, c = 3+2; int d = b+c; int e = a = 5;
    Из примеров видно, что инициализатором может быть не только константа, но и арифметическое выражение. Иногда это выражение может быть вычислено во время компиляции (такое как 3+2), тогда компилятор сразу записывает результат. Иногда это действие откладывается на момент выполнения программы (например, b+c). В последнем случае нескольким переменным присваивается одно и то же значение, однако объявляется лишь первая из них (в данном примере е), остальные уже должны существовать.
    Резюмируем: объявление переменных и возможная инициализация при объявлении описываются следующим образом. Сначала указывается тип переменной, затем ее имя и, если необходимо, инициализатор, который может быть константой или выражением, вычисляемым во время компиляции или исполнения программы. В частности, можно пользоваться уже объявленными переменными. Далее можно поставить запятую и объявить новую переменную точно такого же типа.
    После объявления переменная может применяться в различных выражениях, в которых будет браться ее текущее значение. Также в любой момент можно изменить значение, используя оператор присваивания, примерно так же, как это делалось в инициализаторах.
    Кроме того, при объявлении переменной может быть использовано ключевое слово final. Его указывают перед типом переменной, и тогда ее необходимо сразу инициализировать и уже больше никогда не менять ее значение. Таким образом, final-переменные становятся чем-то вроде констант, но на самом деле некоторые инициализаторы могут вычисляться только во время исполнения программы, генерируя различные значения.
    Простейший пример объявления final-переменной:
    final double pi=3.1415;

    Поля класса

  • name

  • protected String name;
  • size

  • protected int size;
  • style

  • protected int style;

    Получение значения параметров

    До сих пор наши аплеты не получали параметров из документов HTML, в которые мы их встраивали.
    Конечно, все константы, текстовые строки, адреса URL и другую информацию можно закодировать непосредственно в исходном тексте аплета, однако, очевидно, это очень неудобно.
    Пользуясь операторами , расположенными в документе HTML сразу после оператора , можно передать аплету произвольное количество параметров, например, в виде текстовых строк:
    . . .
    Здесь через параметр NAME оператора передается имя параметра аплета, а через параметр VALUE - значение соответствующего параметра.
    Как параметр может получить значение параметров?
    Для получения значения любого параметра аплет должен использовать метод getParameter. В качестве единственного параметра этому методу передается имя параметра аплета в виде строки типа String, например:
    parm_TestString = getParameter("TestString");
    Обычно в аплете также определяется метод getParameterInfo, возвращающий информацию о параметрах. Вот исходный текст этого метода для нашего аплета Draw:
    public String[][] getParameterInfo() { String[][] info = { { "TestString", "String", "Test string" } }; return info; }
    Метод getParameterInfo возвращает массив массивов текстовых строк. Первая строка указывает имя параметра, вторая - тип передаваемых через него данных, а третья - значение параметра по умолчанию.

    Примитивные и ссылочные типы данных

    Теперь на примере переменных можно проиллюстрировать различие между примитивными и ссылочными типами данных. Рассмотрим пример, когда объявляются две переменные одного типа, приравниваются друг другу, а затем значение одной из них изменяется. Что произойдет со второй переменной?
    Возьмем простой тип int:
    int a=5; // объявляем первую переменную и // инициализируем ее int b=a; // объявляем вторую переменную и // приравниваем ее к первой a=3; // меняем значение первой print(b); // проверяем значение второй
    Здесь и далее мы считаем, что функция print(...) позволяет нам некоторым (неважно, каким именно) способом узнать значение ее аргумента (как правило, для этого используют функцию из стандартной библиотеки System.out.println(...), которая выводит значение на системную консоль).
    В результате мы увидим, что значение переменной b не изменилось, оно осталось равным 5. Это означает, что переменные простого типа хранят непосредственно свои значения и при приравнивании двух переменных происходит копирование данного значения. Чтобы еще раз подчеркнуть эту особенность, приведем еще один пример:
    byte b=3; int a=b;
    В данном примере происходит преобразование типов (оно подробно рассматривается в соответствующей лекции). Для нас сейчас важно констатировать, что переменная b хранит значение 3 типа byte, а переменная a – значение 3 типа int. Это два разных значения, и во второй строке при присваивании произошло копирование.
    Теперь рассмотрим ссылочный тип данных. Переменные таких типов всегда хранят ссылки на некоторые объекты. Рассмотрим для примера класс, описывающий точку на координатной плоскости с целочисленными координатами. Описание класса – это отдельная тема, но в нашем простейшем случае оно тривиально:
    class Point { int x, y; }
    Теперь составим пример, аналогичный приведенному выше для int-переменных, считая, что выражение new Point(3,5) создает новый объект-точку с координатами (3,5).
    Point p1 = new Point(3,5); Point p2=p1; p1.x=7; print(p2.x);
    В третьей строке мы изменили горизонтальную координату точки, на которую ссылалась переменная p1, и теперь нас интересует, как это сказалось на точке, на которую ссылается переменная p2. Проведя такой эксперимент, можно убедиться, что в этот раз мы увидим обновленное значение. То есть объектные переменные после приравнивания остаются "связанными" друг с другом, изменения одной сказываются на другой.
    Таким образом, примитивные переменные являются действительными хранилищами данных. Каждая переменная имеет значение, не зависящее от остальных. Ссылочные же переменные хранят лишь ссылки на объекты, причем различные переменные могут ссылаться на один и тот же объект, как это было в нашем примере. В этом случае их можно сравнить с наблюдателями, которые с разных позиций смотрят на один и тот же объект и одинаково видят все происходящие с ним изменения. Если же один наблюдатель сменит объект наблюдения, то он перестает видеть и изменения, происходящие с прежним объектом:

    Point p1 = new Point(3,5); Point p2=p1; p1 = new Point(7,9); print(p2.x);

    В этом примере мы получим 3, то есть после третьей строки переменные p1 и p2 ссылаются на различные объекты и поэтому имеют разные значения.

    Теперь легко понять смысл литерала null. Такое значение может принять переменная любого ссылочного типа. Это означает, что ее ссылка никуда не указывает, объект отсутствует. Соответственно, любая попытка обратиться к объекту через такую переменную (например, вызвать метод или взять значение поля) приведет к ошибке.

    Также значение null можно передать в качестве любого объектного аргумента при вызове функций (хотя на практике многие методы считают такое значение некорректным).

    Память в Java с точки зрения программиста представляется не нулями и единицами или набором байтов, а как некое виртуальное пространство, в котором существуют объекты. И доступ к памяти осуществляется не по физическому адресу или указателю, а лишь через ссылки на объекты. Ссылка возвращается при создании объекта и далее может быть сохранена в переменной, передана в качестве аргумента и т.д. Как уже говорилось, допускается наличие нескольких ссылок на один объект. Возможна и противоположная ситуация – когда на какой-то объект не существует ни одной ссылки. Такой объект уже недоступен программе и является "мусором", то есть без толку занимает аппаратные ресурсы. Для их освобождения не требуется никаких усилий. В состав любой виртуальной машины обязательно входит автоматический сборщик мусора garbage collector – фоновый процесс, который как раз и занимается уничтожением ненужных объектов.

    Очень важно помнить, что объектная переменная, в отличие от примитивной, может иметь значение другого типа, не совпадающего с типом переменной. Например, если тип переменной – некий класс, то переменная может ссылаться на объект, порожденный от наследника этого класса. Все случаи подобного несовпадения будут рассмотрены в следующих разделах курса.

    Теперь рассмотрим примитивные и ссылочные типы данных более подробно.

    Примитивные типы

    Как уже говорилось, существует 8 простых типов данных, которые делятся на целочисленные (integer), дробные (floating-point) и булевские (boolean).

    Прямоугольники и квадраты

    Среди методов класса Graphics есть несколько, предназначенных для рисования прямоугольников. Первый из них, с именем drawRect, позволяет нарисовать прямоугольник, заданный координатами своего левого верхнего угла, шириной и высотой:
    public void drawRect(int x, int y, int width, int height);
    Параметры x и y задают, соответственно, координаты верхнего левого угла, а параметры width и height - высоту и ширину прямоугольника (рис. 2).
    Прямоугольники и квадраты
    Рис. 2. Рисование прямоугольника
    В отличие от метода drawRect, рисующего только прямоугольную рамку, метод fillRect рисует заполненный прямоугольник. Для рисования и заполнения прямоугольника используется цвет, выбранный в контекст отображения (рис. 3).
    Прямоугольники и квадраты
    Рис. 3. Рисование заполненного прямоугольника
    Прототип метода fillRect приведен ниже:
    public abstract void fillRect(int x, int y, int width, int height);
    Метод drawRoundRect позволяет нарисовать прямоугольник с закругленными углами:
    public abstract void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight);
    Параметры x и y определяют координаты верхнего левого угла прямоугольника, параметры width и height задают, соответственно его ширину и высоту.
    Размеры эллипса, образующего закругления по углам, вы можете задать с помощью параметров arcWidth и arcHeight. Первый из них задает ширину эллипса, а второй - высоту (рис. 4).
    Прямоугольники и квадраты
    Рис. 4. Рисование прямоугольника с закругленными углами
    Метод fillRoundRect позволяет нарисовать заполненный прямоугольник с закругленными углами (рис. 5).
    Прямоугольники и квадраты
    Рис. 5. Рисование заполненного прямоугольника с закругленными углами
    Назначение параметров этого метода аналогично назначению параметров только что рассмотренного метода drawRoundRect:
    public abstract void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight);
    Метод fill3Drect предназначен для рисования выступающего или западающего прямоугольника:
    public void fill3DRect(int x, int y, int width, int height, boolean raised);
    Если значение параметра raised равно true, рисуется выступающий прямоугольник, если false - западающий. Назначение остальных параметров аналогично назначению параметров метода drawRect.

    Проект для аплета Draw

    Назад Вперед
    Подготовьте файлы проекта аплета Draw, скопировав их из предыдущего раздела в какой-нибудь каталог. Затем запустите мастер проектов и в соответствующей диалоговой панели укажите путь к этому каталогу (рис. 12).

    Проект для аплета Draw Рис. 12. Указание пути к каталогу с исходными файлами
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    Так как в каталоге уже есть файлы, вы должны включить переключатель Yes и затем нажать кнопку Next.
    После этого вы увидите на экране диалоговую панель, показанную на рис. 13.

    Проект для аплета Draw Рис. 13. Добавление к проекту существующих файлов
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    Здесь вам нужно нажать кнопку Add All in Directory. Как только вы это сделаете, в списке файлов Project Files появится имя файла draw.java, подготовленного вами заранее.
    На следующем шаге вам опять нужно нажать кнопку Next и проследить, чтобы имя главного класса аплета, отображаемое в диалоговой панели, показанной на рис. 14, было draw.

    Проект для аплета Draw Рис. 14. Задание имени главного класса
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    Нажав кнопку Finish, вы можете завершить формирование проекта.
    Hаш аплет принимает из документа HTML один параметр с именем TestString. Для добавления параметров вам нужно открыть окно менеджера проектов, выбрав из меню Project строку Show Project Manager. В этом окне проект draw должен быть текущим.
    Выделите его курсором мыши в окне Java WorkShop Project Manager и из меню Project этого окна выберите строку Edit. Вы увидите блокнот Project Edit, с помощью которого можно изменять различные параметры проекта. Откройте в этом блокноте страницу Run (рис. 15).

    Проект для аплета Draw Рис. 15. Добавление параметра TestString
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    В полях Name и Value введите, соответственно, имя параметра и значение параметра по умолчанию, а затем нажмите кнопку Add. Добавленный вами параметр появится в списке Parameters. Теперь вам осталось только нажать кнопку OK.

    Рисование геометрических фигур

    Назад Вперед
    В этом разделе мы опишем методы класса Graphics, предназначенные для рисования элементарных геометрических фигур, таких как линии, прямоугольники, окружности и так далее.

    Рисование в окне аплета

    Назад Вперед
    В предыдущем разделе мы привели простейший пример аплета, который выполняет рисование текстовой строки в своем окне. Теперь мы расскажем вам о том, что и как может рисовать аплет.
    Способ, которым аплет выполняет рисование в своем окне, полностью отличается от того, которым пользуются программы MS-DOS. Вместо того чтобы обращаться напрямую или через драйвер к регистрам видеоконтроллера, аплет пользуется методами из класса Graphics. Эти методы инкапсулируют все особенности аппаратуры, предоставляя в распоряжение программиста средство рисования, которое пригодно для любой компьютерной платформы.
    Для окна аплета создается объект класса Graphics, ссылка на который передается методу paint. Раньше мы уже пользовались этим объектом, вызывая для него метод drawString, рисующий в окне текстовую строку. Объект, ссылка на который передается методу paint, и есть контекст отображения. Сейчас мы займемся контекстом отображения вплотную.
    Назад Вперед

    Рисование в окне аплетаРисование в окне аплета Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Рисование в окне аплета
    printmenus();
    Рисование в окне аплетаПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Рисование в окне аплета»


    Рисование в окне аплетаSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Рисование в окне аплета»


    Рисование в окне аплетаКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Рисование в окне аплета»


    Рисование в окне аплетаГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Рисование в окне аплета»


    Рисование в окне аплетаОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Рисование в окне аплета»


    Рисование в окне аплетаПроекты
    События
    Lab Downloads
    Посмотреть все Рисование в окне аплета»


    Рисование в окне аплета

    Сегменты

    Метод drawArc предназначен для рисования незаполненного сегмента (рис. 10). Прототип этого метода приведен ниже:
    public abstract void drawArc( int x, int y, int width, int height, int startAngle, int arcAngle);
    Сегменты
    Рис. 10. Рисование незаполненного сегмента
    Параметры x, y, width и height задают координаты прямоугольника, в который вписан сегмент.
    Параметры startAngle и arcAngle задаются в градусах. Они определяют, соответственно, начальный угол и угол разворота сегмента.
    Для того чтобы нарисовать заполненный сегмент, вы можете воспользоваться методом fillArc:
    public abstract void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle);

    Ссылочные типы

    Итак, выражение ссылочного типа имеет значение либо null, либо ссылку, указывающую на некоторый объект в виртуальной памяти JVM.

    ToString()

    Этот метод позволяет получить текстовое описание любого объекта. Создавая новый класс, данный метод можно переопределить и возвращать более подробное описание. Для класса Object и его наследников, не переопределивших toString(), метод возвращает следующее выражение:
    getClass().getName()+"@"+hashCode()
    Метод getName() класса Class уже приводился в пример, а хэш-код еще дополнительно обрабатывается специальной функцией для представления в шестнадцатеричном формате.
    Например:
    print(new Object());
    Результатом будет:
    java.lang.Object@92d342
    В результате этот метод позволяет по текстовому описанию понять, от какого класса был порожден объект и, благодаря хеш-коду, различать разные объекты, созданные от одного класса.
    Именно этот метод вызывается при конвертации объекта в текст, когда он передается в качестве аргумента оператору конкатенации строк.

    Установка атрибутов контекста отображения

    Назад Вперед
    Изменяя атрибуты контекста отображения, приложение Java может установить цвет для рисования графических изображений, таких как линии и многоугольники, шрифт для рисования текста, режим рисования и маску. Возможен также сдвиг начала системы координат.

    Java является строго типизированным языком.

    Java является строго типизированным языком. Это означает, что любая переменная и любое выражение имеют известный тип еще на момент компиляции. Такое строгое правило позволяет выявлять многие ошибки уже во время компиляции. Компилятор, найдя ошибку, указывает точное место (строку) и причину ее возникновения, а динамические "баги" (от английского bugs) необходимо сначала выявить с помощью тестирования (что может потребовать значительных усилий), а затем найти место в коде, которое их породило. Поэтому четкое понимание модели типов данных в Java очень помогает в написании качественных программ.
    Все типы данных разделяются на две группы. Первую составляют 8 простых, или примитивных (от английского primitive), типов данных. Они подразделяются на три подгруппы:

  • целочисленные
  • byte
  • short
  • int
  • long
  • char (также является целочисленным типом)

  • дробные
  • float
  • double

  • булевский
  • boolean

  • Вторую группу составляют объектные, или ссылочные (от английского reference), типы данных. Это все классы, интерфейсы и массивы. В стандартных библиотеках первых версий Java находилось несколько сот классов и интерфейсов, сейчас их уже тысячи. Кроме стандартных, написаны многие и многие классы и интерфейсы, составляющие любую Java-программу.
    Иллюстрировать логику работы с типами данных проще всего на примере переменных.

    Выбор цвета

    Изменение цвета, выбранного в контекст отображения, выполняется достаточно часто. В классе Graphics для изменения цвета определен метод setColor, прототип которого представлен ниже:
    public abstract void setColor(Color c);
    В качестве параметра методу setColor передается ссылка на объект класса Color, с помощью которого можно выбрать тот или иной цвет.
    Как задается цвет?
    Для этого можно использовать несколько способов.
    Прежде всего, вам доступны статические объекты, определяющие фиксированный набор основных цветов:

    Объект Цвет
    public final static Color black; черный
    public final static Color blue; голубой
    public final static Color cyan; циан
    public final static Color darkGray; темно-серый
    public final static Color gray; серый
    public final static Color green; зеленый
    public final static Color lightGray; светло-серый
    public final static Color magenta; малиновый
    public final static Color orange; оранжевый
    public final static Color pink; розовый
    public final static Color red; красный
    public final static Color white; белый
    public final static Color yellow; желтый

    Этим набором цветов пользоваться очень просто:
    public void paint(Graphics g) { g.setColor(Color.yellow); g.drawString("Hello, Java world!", 10, 20); . . . }
    Здесь мы привели фрагмент исходного текста метода paint, в котором в контексте отображения устанавливается желтый цвет. После этого метод drawString выведет текстовую строку " Hello, Java world!" желтым цветом.
    Если необходима более точная установка цвета, вы можете воспользоваться одним из трех конструкторов объекта Color:
    public Color(float r, float g, float b); public Color(int r, int g, int b); public Color(int rgb);
    Первые два конструктора позволяют задавать цвет в виде совокупности значений трех основных цветовых компонент - красной, желтой и голубой (соответственно, параметры r, g и b). Для первого конструктора диапазон возможных значений компонент цвета находится в диапазоне от 0.0 до 1.0, а для второго - в диапазоне от 0 до 255.

    Выбор шрифта

    С помощью метода setFont из класса Graphics вы можете выбрать в контекст отображения шрифт, который будет использоваться методами drawString, drawBytes и drawChars для рисования текста. Вот прототип метода setFont:
    public abstract void setFont(Font font);
    В качестве параметра методу setFont следует передать объект класса Font.

    Задание области ограничения

    Если для окна аплета задать область ограничения, то рисование будет возможно только в пределах этой области. Область ограничения задается методом clipRect, прототип которого мы привели ниже:
    public abstract void clipRect( int x, int y, int width, int height);
    Параметры x, y, width и height задают координаты прямоугольной области ограничения.

    одна из ключевых тем курса.

    Типы данных – одна из ключевых тем курса. Невозможно написать ни одной программы, не используя их. Вот список некоторых операций, где применяются типы:
  • объявление типов;
  • создание объектов;
  • при объявлении полей – тип поля;
  • при объявлении методов – входные параметры, возвращаемое значение;
  • при объявлении конструкторов – входные параметры;
  • оператор приведения типов;
  • оператор instanceof;
  • объявление локальных переменных;
  • многие другие – обработка ошибок, import-выражения и т.д.

  • Принципиальные различия между примитивными и ссылочными типами данных будут рассматриваться и дальше по ходу курса. Изучение объектной модели Java даст основу для более подробного изложения объектных типов – обычных и абстрактных классов, интерфейсов и массивов. После приведения типов будут описаны связи между типом переменной и типом ее значения.
    В обсуждении будущей версии Java 1.5 упоминаются темплейты (templates), которые существенно расширят понятия типа данных, если действительно войдут в стандарт языка.
    В лекции было рассказано о том, что Java является строго типизированным языком, то есть тип всех переменных и выражений определяется уже компилятором. Это позволяет существенно повысить надежность и качество кода, а также делает необходимым понимание программистами объектной модели.
    Все типы в Java делятся на две группы – фиксированные простые, или примитивные, типы (8 типов) и многочисленная группа объектных типов (классов). Примитивные типы действительно являются хранилищами данных своего типа. Ссылочные переменные хранят ссылку на некоторый объект совместимого типа. Они также могут принимать значение null, не указывая ни на какой объект. JVM подсчитывает количество ссылок на каждый объект и активизирует механизм автоматической сборки мусора для удаления неиспользуемых объектов.
    Были рассмотрены переменные. Они характеризуются тремя основными параметрами – имя, тип и значение. Любая переменная должна быть объявлена и при этом может быть инициализирована. Возможно использование модификатора final.
    Примитивные типы состоят из пяти целочисленных, включая символьный тип, двух дробных и одного булевского. Целочисленные литералы имеют ограничения, связанные с типами данных. Были рассмотрены все операторы над примитивными типами, тип возвращаемого значения и тонкости их использования.
    Затем изучались объекты, способы их создания и операторы, выполняющие над ними различные действия, в частности принцип работы оператора instanceof. Далее были рассмотрены самые главные классы в Java – Object, Class, String.

    Программирование на Java

    Аплет LineDraw

    Назад Вперед
    В аплете LineDraw мы показали основные приемы обработки событий, вызываемых мышью. Вы можете рисовать в окне аплета прямые линии, причем возле концов линий отображаются их координаты (рис. 1).
    Аплет LineDraw
    Рис. 1. Окно аплета LineDraw с нарисованными в нем линиями
    Для того чтобы нарисовать линию в окне аплета LineDraw, вы должны установить курсор в начальную точку, нажать клавишу мыши и затем, не отпуская ее, переместить курсор в конечную точку. После отпускания клавиши мыши координаты линии будут сохранены аплетом в массиве, после чего произойдет перерисовка окна аплета.
    По мере того как вы будете рисовать линии, аплет будет заполнять массив с координатами линий. Каждый раз, когда окно аплета будет перерисовываться, метод paint перерисует все линии заново, пользуясь координатами, сохраненными в массиве.
    Для того чтобы стереть содержимое окна аплета, вам достаточно сделать двойной щелчок в его окне. При этом из массива координать линий будут удалены все элементы.
    Назад Вперед

    Аплет LineDrawАплет LineDraw Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Аплет LineDraw
    printmenus();
    Аплет LineDrawПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Аплет LineDraw»


    Аплет LineDrawSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Аплет LineDraw»


    Аплет LineDrawКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Аплет LineDraw»


    Аплет LineDrawГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Аплет LineDraw»


    Аплет LineDrawОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Аплет LineDraw»


    Аплет LineDrawПроекты
    События
    Lab Downloads
    Посмотреть все Аплет LineDraw»


    Аплет LineDraw

    Имена и идентификаторы

    Теперь, когда мы рассмотрели простые и составные имена, уточним разницу между идентификатором (напомним, что это вид лексемы) и именем. Понятно, что простое имя состоит из одного идентификатора, а составное - из нескольких. Однако не всякий идентификатор входит в состав имени.
    Во-первых, в выражении объявления (declaration) идентификатор еще не является именем. Другими словами, он становится именем после первого появления в коде в месте объявления.
    Во-вторых, существует возможность обращаться к полям и методам объектного типа не через имя типа или объектной переменной, а через ссылку на объект, полученную в результате выполнения выражения. Пример такого вызова:
    country.getCity().getStreet();
    В данном примере getStreet является не именем, а идентификатором, так как соответствующий метод вызывается у объекта, полученного в результате вызова метода getCity(). Причем country.getCity как раз является составным именем метода.
    Наконец, идентификаторы также используются для названий меток (label). Эта конструкция рассматривается позже, однако приведем пример, показывающий, что пространства имен и названий меток полностью разделены.
    num: for (int num = 2; num <= 100; num++) { int n = (int)Math.sqrt(num)+1; while (--n != 1) { if (num%n==0) { continue num; } } System.out.print(num+" "); }
    Результатом будут простые числа меньше 100:
    2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
    Мы видим, что здесь применяются одноименные переменная и метка num, причем последняя используется для выхода из внутреннего цикла while на внешний for.
    Очевидно, что удобнее использовать простое имя, а не составное, т.к. оно короче и его легче запомнить. Однако понятно, что если в системе есть очень много классов со множеством переменных, можно столкнуться с ситуацией, когда в разных классах есть одноименные переменные или методы. Для решения этой и других подобных проблем вводится новое понятие – область видимости.

    Импорт-выражения

    Как будет рассмотрено ниже, область видимости объявления типа - пакет, в котором он располагается. Это означает, что внутри данного пакета допускается обращение к типу по его простому имени. Из всех других пакетов необходимо обращаться по составному имени, то есть полное имя пакета плюс простое имя типа, разделенные точкой. Поскольку пакеты могут иметь довольно длинные имена (например, дополнительный пакет в составе JDK1.2 называется com.sun.image.codec.jpeg), а тип может многократно использоваться в модуле компиляции, такое ограничение может привести к усложнению исходного кода и сложностям в разработке.
    Для решения этой проблемы вводятся import-выражения, позволяющие импортировать типы в модуль компиляции и далее обращаться к ним по простым именам. Существует два вида таких выражений:
  • импорт одного типа;
  • импорт пакета.

  • Важно подчеркнуть, что импортирующие выражения являются, по сути, подсказкой компилятора. Он пользуется ими, чтобы для каждого простого имени типа из другого пакета получить его полное имя, которое и попадает в компилированный код. Это означает, что импортирующих выражений может быть очень много, включая и те, что импортируют неиспользуемые пакеты и типы, но это никак не отразится ни на размере, ни на качестве бинарного кода. Также безразлично, обращаться к типу по его полному имени, или включить его в импортирующее выражение и обращаться по простому имени – результат будет один и тот же.
    Импортирующие выражения имеют эффект только внутри модуля компиляции, в котором они объявлены. Все объявления типов высшего уровня, находящиеся в этом же модуле, могут одинаково пользоваться импортированными типами. К импортированным типам возможен и обычный доступ по полному имени.
    Выражение, импортирующее один тип, записывается с помощью ключевого слова import и полного имени типа. Например:
    import java.net.URL;
    Такое выражение означает, что в дальнейшем в этом модуле компиляции простое имя URL будет обозначать одноименный класс из пакета java.net. Попытка импортировать тип, недоступный на момент компиляции, вызовет ошибку. Если один и тот же тип импортируется несколько раз, то это не создает ошибки, а дублированные выражения игнорируются. Если же импортируются типы с одинаковыми простыми именами из разных пакетов, то такая ситуация породит ошибку компиляции.
    Выражение, импортирующее пакет, включает в себя полное имя пакета следующим образом.

    import java.awt.*;

    Это выражение делает доступными все типы, находящиеся в пакете java.awt, по их простому имени. Попытка импортировать пакет, недоступный на момент компиляции, вызовет ошибку. Импортирование одного пакета многократно не создает ошибки, дублированные выражения игнорируются. Обратите внимание, что импортировать вложенный пакет нельзя.

    Например:

    // пример вызовет ошибку компиляции import java.awt.image;

    Создается впечатление, что теперь мы можем обращаться к типам пакета java.awt.image по упрощенному имени, например, image.ImageFilter. На самом деле пример вызовет ошибку компиляции, так как данное выражение расценивается как импорт типа, а в пакете java.awt отсутствует тип image.

    Аналогично, выражение

    import java.awt.*;

    не делает более доступными классы пакета java.awt.image, их необходимо импортировать отдельно.

    Поскольку пакет java.lang содержит типы, без которых невозможно создать ни одну программу, он неявным образом импортируется в каждый модуль компиляции. Таким образом, все типы из этого пакета доступны по их простым именам без каких-либо дополнительных усилий. Попытка импортировать данный пакет еще раз будет проигнорирована.

    Допускается одновременно импортировать пакет и какой-нибудь тип из него:

    import java.awt.*; import java.awt.Point;

    Может возникнуть вопрос, как же лучше поступать – импортировать типы по отдельности или весь пакет сразу? Есть ли какая-нибудь разница в этих подходах?

    Разница заключается в алгоритме работы компилятора, который приводит каждое простое имя к полному. Он состоит из трех шагов:

  • сначала просматриваются выражения, импортирующие типы;
  • затем другие типы, объявленные в текущем пакете, в том числе в текущем модуле компиляции;
  • наконец, просматриваются выражения, импортирующие пакеты.


  • Таким образом, если тип явно импортирован, то невозможно ни объявление нового типа с таким же именем, ни доступ по простому имени к одноименному типу в текущем пакете.

    Например:

    // пример вызовет ошибку компиляции package my_geom;


    import java.awt.Point;

    class Point { }

    Этот модуль вызовет ошибку компиляции, так как имя Point в объявлении высшего типа будет рассматриваться как обращение к импортированному классу java.awt.Point, а его переопределять, конечно, нельзя.

    Если в пакете объявлен тип:

    package my_geom;

    class Point { }

    то в другом модуле компиляции:

    package my_geom;

    import java.awt.Point;

    class Line { void main() { System.out.println(new Point()); } }

    складывается неопределенная ситуация – какой из классов, my_geom.Point или java.awt.Point, будет использоваться при создании объекта? Результатом будет:

    java.awt.Point[x=0,y=0]

    В соответствии с правилами, имя Point было трактовано на основе импорта типа. К классу текущего пакета все еще можно обращаться по полному имени: my_geom.Point. Если бы рассматривался безымянный пакет, то обратиться к такому "перекрытому" типу было бы уже невозможно, что является дополнительным аргументом к рекомендации располагать важные программы в именованных пакетах.

    Теперь рассмотрим импорт пакета. Его еще называют "импорт по требованию", подразумевая, что никакой "загрузки" всех типов импортированного пакета сразу при указании импортирующего выражения не происходит, их полные имена подставляются по мере использования простых имен в коде. Можно импортировать пакет и задействовать только один тип (или даже ни одного) из него.

    Изменим рассмотренный выше пример:

    package my_geom;

    import java.awt.*;

    class Line { void main() { System.out.println(new Point()); System.out.println(new Rectangle()); } }

    Теперь результатом будет:

    my_geom.Point@92d342 java.awt.Rectangle[x=0,y=0,width=0,height=0]

    Тип Point нашелся в текущем пакете, поэтому компилятору не пришлось выполнять поиск по пакету java.awt. Второй объект порождается от класса Rectangle, которого не существует в текущем пакете, зато он обнаруживается в java.awt.

    Также корректен теперь пример:

    package my_geom;

    import java.awt.*;

    class Point { }

    Таким образом, импорт пакета не препятствует объявлению новых типов или обращению к существующим типам текущего пакета по простым именам. Если все же нужно работать именно с внешними типами, то можно воспользоваться импортом типа, или обращаться к ним по полным именам. Кроме того, считается, что импорт конкретных типов помогает при прочтении кода сразу понять, какие внешние классы и интерфейсы используются в этом модуле компиляции. Однако полностью полагаться на такое соображение не стоит, так как возможны случаи, когда импортированные типы не используются и, напротив, в коде стоит обращение к другим типам по полному имени.

    Исходный текст аплета LineDraw

    Назад Вперед
    Исходный текст аплета LieDrnaw вы найдете в листинге 1.
    Листинг 1. Файл LieDrnaw.java
    import java.applet.*; import java.awt.*; import java.util.*;
    public class LineDraw extends Applet { Dimension dmDown; Dimension dmUp; Dimension dmPrev; boolean bDrawing; Vector lines;
    public String getAppletInfo() { return "Name: LineDraw"; }
    public void init() { bDrawing = false; lines = new Vector(); }
    public void paint(Graphics g) { Dimension dimAppWndDimension = getSize();
    setBackground(Color.yellow); g.setColor(Color.black); g.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);
    for (int i=0; i < lines.size(); i++) { Rectangle p = (Rectangle)lines.elementAt(i); g.drawLine(p.width, p.height, p.x, p.y); g.drawString("<" + p.width + "," + p.height + ">", p.width, p.height); g.drawString("<" + p.x + "," + p.y+ ">", p.x, p.y); } bDrawing = false; }
    public boolean mouseDown(Event evt, int x, int y) { if(evt.clickCount > 1) { lines.removeAllElements(); repaint(); return true; }
    dmDown = new Dimension(x, y); dmPrev = new Dimension(x, y); bDrawing = false; return true; }
    public boolean mouseUp(Event evt, int x, int y) { if(bDrawing) { dmUp = new Dimension(x, y); lines.addElement( new Rectangle(dmDown.width, dmDown.height, x, y)); repaint(); bDrawing = false; } return true; }
    public boolean mouseDrag(Event evt, int x, int y) { Graphics g = getGraphics(); bDrawing = true;
    g.setColor(Color.yellow); g.drawLine(dmDown.width, dmDown.height, dmPrev.width, dmPrev.height);
    g.setColor(Color.black); g.drawLine(dmDown.width, dmDown.height, x, y);
    dmPrev = new Dimension(x, y); return true; }
    public boolean mouseMove(Event evt, int x, int y) { bDrawing = false; return true; } }
    Исходный текст документа HTML, подготовленного системой JavaWorkshop, представлен в листинге 2.
    Листинг 2. Файл LineDraw.tmp.html


    Элементы пакета

    Еще раз повторим, что элементами пакета являются вложенные пакеты и типы (классы и интерфейсы). Одноименные элементы запрещены, то есть не может быть одноименных класса и интерфейса, или вложенного пакета и типа. В противном случае возникнет ошибка компиляции.
    Например, в JDK 1.0 пакет java содержал пакеты applet, awt, io, lang, net, util и не содержал ни одного типа. В пакет java.awt входил вложенный пакет image и 46 классов и интерфейсов.
    Составное имя любого элемента пакета – это составное имя этого пакета плюс простое имя элемента. Например, для класса Object в пакете java.lang составным именем будет java.lang.Object, а для пакета image в пакете java.awt – java.awt.image.
    Иерархическая структура пакетов была введена для удобства организации связанных пакетов, однако вложенные пакеты, или соседние, то есть вложенные в один и тот же пакет, не имеют никаких дополнительных связей между собой, кроме ограничения на несовпадение имен. Например, пакеты space.sun, space.sun.ray, space.moon и factory.store совершенно "равны" между собой и типы одного из этих пакетов не имеют никакого особенного доступа к типам других пакетов.

    Как обрабатываются события

    Назад Вперед
    Когда возникает событие, управление получает метод handleEvent из класса Component. Класс Applet является дочерним по отношению к классу Component.
    Прототип метода handleEvent мы привели ниже:
    public boolean handleEvent(Event evt);
    В качестве параметра методу handleEvent передается объект класса Event, который содержит всю информацию о событии. По содержимому полей класса Event вы можете определить координаты курсора мыши в момент, когда пользователь нажал клавишу, отличить одинарный щелчок от двойного и так далее.
    Ниже мы привели список полей класса Event, которые вы можете проанализировать:

    Поле Описание
    public Object argarg; Произвольный аргумент события, значение которого зависит от типа события
    public int clickCountclickCount; Это поле имеет значение только для события с типом MOUSE_DOWNMOUSE_DOWN и содержит количество нажатий на клавишу мыши. Если пользователь сделал двойной щелчок мышью, в это поле будет записано значение 2
    public Event evtevt; Следующее событие в связанном списке
    public int idid; Тип события. Ниже мы перечислим возможные значения для этого поля
    public int keykey; Код нажатой клавиши (только для события, созданного при выполнении пользователем операции с клавиатурой)
    public int modifiersmodifiers; Состояние клавиш модификации , ,
    public Object targettarget; Компонент, в котором произошло событие
    public long whenwhen; Время, когда произошло событие
    public int xx; Координата по оси X
    public int yy; Координата по оси Y

    Поле id (тип события) может содержать следующие значения:

    Значение Тип события
    ACTION_EVENTACTION_EVENT Пользователь хочет, чтобы произошло некоторое событие
    GOT_FOCUSGOT_FOCUS Компонент (в нашем случае окно аплета) получил фокус ввода. О фокусе ввода вы узнаете из раздела, посвященного работе с клавиатурой
    KEY_ACTIONKEY_ACTION Пользователь нажал клавишу типа "Action"
    KEY_ACTION_RELEASEKEY_ACTION_RELEASE Пользователь отпустил клавишу типа "Action"
    KEY_PRESSKEY_PRESS Пользователь нажал обычную клавишу
    KEY_RELEASEKEY_RELEASE Пользователь отпустил обычную клавишу
    LIST_DESELECTLIST_DESELECT Отмена выделения элемента в списке
    LIST_SELECTLIST_SELECT Выделение элемента в списке
    LOAD_FILELOAD_FILE Загрузка файла
    LOST_FOCUSLOST_FOCUS Компонент потерял фокус ввода
    MOUSE_DOWNMOUSE_DOWN Пользователь нажал клавишу мыши
    MOUSE_DRAGMOUSE_DRAG Пользователь нажал клавишу мыши и начал выполнять перемещение курсора мыши
    MOUSE_ENTERMOUSE_ENTER Курсор мыши вошел в область окна аплета
    MOUSE_EXITMOUSE_EXIT Курсор мыши покинул область окна аплета
    MOUSE_MOVEMOUSE_MOVE Пользователь начал выполнять перемещение курсора мыши, не нажимая клавишу мыши
    MOUSE_UPMOUSE_UP Пользователь отпустил клавишу мыши
    SAVE_FILESAVE_FILE Сохранение файла
    SCROLL_ABSOLUTESCROLL_ABSOLUTE Пользователь переместил движок полосы просмотра в новую позицию
    SCROLL_LINE_DOWNSCROLL_LINE_DOWN Пользователь выполнил над полосой просмотра операцию сдвига на одну строку вниз
    SCROLL_LINE_UPSCROLL_LINE_UP Пользователь выполнил над полосой просмотра операцию сдвига на одну строку вверх
    SCROLL_PAGE_DOWNSCROLL_PAGE_DOWN Пользователь выполнил над полосой просмотра операцию сдвига на одну страницу вниз
    SCROLL_PAGE_UPSCROLL_PAGE_UP Пользователь выполнил над полосой просмотра операцию сдвига на одну страницувверх
    WINDOW_DEICONIFYWINDOW_DEICONIFY Пользователь запросил операцию восстановления нормального размера окна после его минимизации
    WINDOW_DESTROYWINDOW_DESTROY Пользователь собирается удалить окно
    WINDOW_EXPOSEWINDOW_EXPOSE Окно будет отображено
    WINDOW_ICONIFYWINDOW_ICONIFY Окно будет минимизировано
    WINDOW_MOVEDWINDOW_MOVED Окно будет перемещено
    <
    Если событие связано с клавиатурой (тип события KEY_ACTION или KEY_ACTION_RELEASE), в поле key может находиться одно из следующих значений:

    Значение Клавиша
    DOWNDOWN Клавиша перемещения курсора вниз
    ENDEND
    F1F1-F12
    F2
    F3
    F4
    F5
    F6
    F7
    F8
    F9
    F10
    F11
    F12
    HOMEHOME
    LEFTLEFT Клавиша перемещения курсора влево
    PGDNPGDN
    PGUPPGUP
    RIGHTRIGHT Клавиша перемещения курсора вправо
    UPUP Клавиша перемещения курсора вверх
    Могут быть указаны следующие маски для поля модификаторов modifiers:

    Значение маски Описание
    ALT_MASKALT_MASK Была нажата клавиша
    META_MASKMETA_MASK Была нажата мета-клавиша (клавиша для ввода диактрических символов)
    CTRL_MASKCTRL_MASK Была нажата клавиша
    SHIFT_MASKSHIFT_MASK Была нажата клавиша
    Ваше приложение может переопределить метод handleEvent и обрабатывать события самостоятельно, однако есть более простой путь. Обработчик этого метода, который используется по умолчанию, вызывает несколько методов, которые более удобны в использовании, в частности, при обработке событий от мыши или клавиатуры.

    Назад Вперед

    Как обрабатываются событияКак обрабатываются события Контакты

    О компании

    Новости

    Вакансии

    Правовые аспекты

    Условия использования

    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.

    Как обрабатываются события

    printmenus();

    Как обрабатываются событияПрограммные продукты

    Рабочие станции и тонкие клиенты

    Серверы

    Системы хранения данных

    Посмотреть все Как обрабатываются события»

    Как обрабатываются событияSolaris 10

    Java 2 Standard Edition

    Developer Tools

    Top Downloads

    New Downloads

    Патчи и обновления

    Посмотреть все Как обрабатываются события»

    Как обрабатываются событияКаталог решений

    Истории успеха

    The Sun Grid

    Партнерские программы

    Посмотреть все Как обрабатываются события»

    Как обрабатываются событияГарантийное обслуживание

    Программы SunSpectrum

    Консалтинг

    Услуги инсталляции

    Поддержка ПО

    Метод init

    Метод init сбрасывает признак рисования, записывая в поле bDrawing значение false, а также создает новый динамический массив в виде объекта класса Vector:
    public void init() { bDrawing = false; lines = new Vector(); }

    Метод mouseDown

    В начале своей работы метод mouseDown определяет, был ли сделан одинарный щелчок клавишей мыши, или двойной. Если был сделан двойной щелчок мышью, метод удаляет все элементы из массива list, а затем перерисовывает окно аплета, вызывая метод repaint:
    lines.removeAllElements(); repaint();
    После перерисовки окно аплета очищается от линий.
    Если же был сделан одинарный щелчок клавишей мыши, метод mouseDown сохраняет текущие координаты курсора в переменных dmDown и dmPrev, а затем сбрасывает признак рисования:
    dmDown = new Dimension(x, y); dmPrev = new Dimension(x, y); bDrawing = false;

    Метод mouseDrag

    До сих пор наши аплеты выполняли рисование только в методе paint, и так поступают большинство аплетов. Однако наш аплет должен рисовать линии во время перемещения курсора мыши, так как в противном случае пользователю не будет видно, как пройдет рисуемая линия.
    Для того чтобы нарисовать что-либо в окне аплета, наобходимо получить контекст отображения. Методу paint этот контекст передается через парметр как объект класса Graphics. Если же вы собираетесь рисовать в другом методе, отличном от paint, необходимо получить контекст отображения, например, так:
    Graphics g = getGraphics();
    После получения контекста отображения и включения режима рисования (записью в переменную bDrawing значения true) метод mouseDrag стирает линию, которая была нарисована ранее, в процессе предыдущего вызова этого же метода:
    g.setColor(Color.yellow); g.drawLine(dmDown.width, dmDown.height, dmPrev.width, dmPrev.height);
    Для стирания линии мы рисуем ее на том же месте с использованием цвета, совпадающего с цветом фона.
    Далее метод mouseDrag рисует новую линию черного цвета, соединяя точку, в которой была нажата клавиша мыши, с точкой текущего расположения курсора мыши:
    g.setColor(Color.black); g.drawLine(dmDown.width, dmDown.height, x, y);
    После рисования линии координаты ее конца сохраняются в поле dmPrev для стирания этой линии при следующем вызове метода mouseDrag:
    dmPrev = new Dimension(x, y); return true;

    Метод mouseMove

    Метод mouseMove не делает ничего, за исключением того, что он отключает режим рисования. Таким образом, простое перемещение курсора мыши над окном аплета не приводит к рисованию линий.
    Назад Вперед

    Метод mouseMoveМетод mouseMove Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Метод mouseMove
    printmenus();
    Метод mouseMoveПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Метод mouseMove»


    Метод mouseMoveSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Метод mouseMove»


    Метод mouseMoveКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Метод mouseMove»


    Метод mouseMoveГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Метод mouseMove»


    Метод mouseMoveОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Метод mouseMove»


    Метод mouseMoveПроекты
    События
    Lab Downloads
    Посмотреть все Метод mouseMove»


    Метод mouseMove

    Метод mouseUp

    Когда пользователь отпускает клавишу мыши, вызывается метод mouseUp. В его задачу входит сохранение текущих координат курсора мыши в поле dmUp, а также добавление нового элемента в массив lines:
    dmUp = new Dimension(x, y); lines.addElement( new Rectangle(dmDown.width, dmDown.height, x, y)); repaint();
    После добавления элемента в массив метод mouseUp инициирует перерисовку окна аплета, вызывая для этого метод repaint.
    Заметим, что в качестве координат начала линии мы записываем в элемент массива координаты точки, где в последний раз пользователь нажимал курсор мыши. В качестве координат конца линии используются текущие координаты курсора на момент отпускания клавиши мыши.

    Метод paint

    После изменения цвета фона и рисования рамки метод paint перебирает в цикле все элементы массива lines, рисуя линии:
    for(int i=0; i < lines.size(); i++) { Rectangle p = (Rectangle)lines.elementAt(i);
    g.drawLine( p.width, p.height, p.x, p.y);
    g.drawString("<" + p.width + "," + p.height + ">", p.width, p.height);
    g.drawString("<" + p.x + "," + p.y+ ">", p.x, p.y); }
    Для объектов класса Vector метод size возвращает количество элементов в массиве, чем мы воспользовались для проверки условия выхода из цикла.
    Чтобы извлечь элемент массива по его номеру, мы применили метод elementAt, передав ему через единственный параметр номер извлекаемого элемента.
    Так как в массиве хранятся объекты класса Rectangle, перед инициализацией ссылки p мы выполняем явное преобразование типов.
    Координаты концов линий рисуются с помощью уже знакомого вам метода drawString.
    Перед завершением работы метод paint сбрасывает признак рисования, записывая в поле bDrawing значение false:
    bDrawing = false;

    Модуль компиляции

    Модуль компиляции (compilation unit) хранится в текстовом .java-файле и является единичной порцией входных данных для компилятора. Он состоит из трех частей:
  • объявление пакета;

  • import-выражения;
  • объявления верхнего уровня.

  • Объявление пакета одновременно указывает, какому пакету будут принадлежать все объявляемые ниже типы. Если данное выражение отсутствует, значит, эти классы располагаются в безымянном пакете (другое название – пакет по умолчанию).
    Import-выражения позволяют обращаться к типам из других пакетов по их простым именам, "импортировать" их. Эти выражения также необязательны.
    Наконец, объявления верхнего уровня содержат объявления одного или нескольких типов. Название "верхнего уровня" противопоставляет эти классы и интерфейсы, располагающиеся в пакетах, внутренним типам, которые являются элементами и располагаются внутри других типов. Как ни странно, эта часть также является необязательной, в том смысле, что в случае ее отсутствия компилятор не выдаст ошибки. Однако никаких .class-файлов сгенерировано тоже не будет.
    Доступность модулей компиляции определяется поддержкой платформы, т.к. утилиты Java являются обычными программами, которые исполняются операционной системой по общим правилам.
    Рассмотрим все три части более подробно.

    Нажатие клавиши мыши

    Переопределив метод mouseDown, вы сможете отслеживать нажатия клавиши мыши. Прототип этого метода приведен ниже:
    public boolean mouseDown(Event evt, int x, int y);
    Через параметр evt методу передается ссылка на объект Event, с помощью которой метод может получить полную информацию о событии.
    Анализируя содержимое параметров x и y, приложение может определить координаты курсора на момент возникновения события.
    Заметим, что для отслеживания двойного щелчка мыши не предусмотрено никакого отдельного метода. Однако анализируя содержимое поля clickCount переменной evt, вы можете определить кратность щелчка мыши:
    if(evt.clickCount > 1) // Двойной щелчок showStatus("Mouse Double Click"); else // Одинарный щелчок showStatus("Mouse Down");

    Объявление пакета

    Первое выражение в модуле компиляции – объявление пакета. Оно записывается с помощью ключевого слова package, после которого указывается полное имя пакета.
    Например, первой строкой (после комментариев) в файле java/lang/Object.java идет:
    package java.lang;
    Это одновременно служит объявлением пакета lang, вложенного в пакет java, и указанием, что объявляемый ниже класс Object находится в данном пакете. Так складывается полное имя класса java.lang.Object.
    Если это выражение отсутствует, то такой модуль компиляции принадлежит безымянному пакету. Этот пакет по умолчанию обязательно должен поддерживаться реализацией Java-платформы. Обратите внимание, что он не может иметь вложенных пакетов, так как составное имя пакета должно обязательно начинаться с имени пакета верхнего уровня.
    Таким образом, самая простая программа может выглядеть следующим образом:
    class Simple { public static void main(String s[]) { System.out.println("Hello!"); } }
    Этот модуль компиляции будет принадлежать безымянному пакету.
    Пакет по умолчанию был введен в Java для облегчения написания очень небольших или временных приложений, для экспериментов. Если же программа будет распространяться для пользователей, то рекомендуется расположить ее в пакете, который, в свою очередь, должен быть правильно назван. Соглашения по именованию рассматриваются ниже.
    Доступность пакета определяется по доступности модулей компиляции, в которых он объявляется. Точнее, пакет доступен тогда и только тогда, когда выполняется любое из следующих двух условий:
  • доступен модуль компиляции с объявлением этого пакета;
  • доступен один из вложенных пакетов этого пакета.

  • Таким образом, для следующего кода:
    package space.star;
    class Sun { }
    если файл, который хранит этот модуль компиляции, доступен Java-платформе, то пакеты space и вложенный в него star (полное название space.star) также становятся доступны для Java.
    Если пакет доступен, то область видимости его объявления – все доступные модули компиляции. Проще говоря, все существующие пакеты доступны для всех классов, никаких ограничений на доступ к пакетам в Java нет.
    Требуется, чтобы пакеты java.lang и java.io, а значит, и java, всегда были доступны для Java-платформы, поскольку они содержат классы, необходимые для работы любого приложения.

    Объявление верхнего уровня

    Далее модуль компиляции может содержать одно или несколько объявлений классов и интерфейсов. Подробно формат такого объявления рассматривается в следующих лекциях, однако приведем краткую информацию и здесь.
    Объявление класса начинается с ключевого слова class, интерфейса – interface. Далее указывается имя типа, а затем в фигурных скобках описывается тело типа. Например:
    package first;
    class FirstClass { }
    interface MyInterface { }
    Область видимости типа - пакет, в котором он описан. Из других пакетов к типу можно обращаться либо по составному имени, либо с помощью импортирующих выражений.
    Однако, кроме области видимости, в Java также есть средства разграничения доступа. По умолчанию тип объявляется доступным только для других типов своего пакета. Чтобы другие пакеты также могли использовать его, можно указать ключевое слово public:
    package second;
    public class OpenClass { }
    public interface PublicInterface { }
    Такие типы доступны для всех пакетов.
    Объявления верхнего уровня описывают классы и интерфейсы, хранящиеся в пакетах. В версии Java 1.1 были введены внутренние (inner) типы, которые объявляются внутри других типов и являются их элементами наряду с полями и методами. Данная возможность является вспомогательной и довольно запутанной, поэтому в курсе подробно не рассматривается, хотя некоторые примеры и пояснения помогут в целом ее освоить.
    Если пакеты, исходный и бинарный код хранятся в файловой системе, то Java может накладывать ограничение на объявления классов в модулях компиляции. Это ограничение создает ошибку компиляции в случае, если описание типа не обнаруживается в файле с названием, составленным из имени типа и расширения (например, java), и при этом:
  • тип объявлен как public и, значит, может использоваться из других пакетов;
  • тип используется из других модулей компиляции в своем пакете.

  • Эти условия означают, что в модуле компиляции может быть максимум один тип отвечающий этим условиям.
    Другими словами, в модуле компиляции может быть максимум один public тип, и его имя и имя файла должны совпадать. Если же в нем есть не-public типы, имена которых не совпадают с именем файла, то они должны использоваться только внутри этого модуля компиляции.
    Если же для хранения пакетов применяется БД, то такое ограничение не должно накладываться.
    На практике же программисты зачастую помещают в один модуль компиляции только один тип, независимо от того, public он или нет. Это существенно упрощает работу с ними. Например, описание класса space.sun.Size хранится в файле space\sun\Size.java, а бинарный код – в файле Size.class в том же каталоге. Именно так устроены все стандартные библиотеки Java.
    Обратите внимание, что при объявлении классов вполне допускаются перекрестные обращения. В частности, следующий пример совершенно корректен:
    package test;
    /* * Класс Human, описывающий человека */ class Human { String name; Car car; // принадлежащая человеку машина }
    /* * Класс Car, описывающий автомобиль */ class Car { String model; Human driver; // водитель, управляющий // машиной }
    Кроме того, класс Car был использован раньше, чем был объявлен. Такое перекрестное применение типов также допускается в случае, если они находятся в разных пакетах. Компилятор должен поддерживать возможность транслировать их одновременно.

    Область видимости имен

    Областью видимости объявления некоторого элемента языка называется часть программы, откуда допускается обращение к этому элементу по простому имени.
    При рассмотрении каждого элемента языка будет указываться его область видимости, однако имеет смысл собрать эту информацию в одном месте.
    Область видимости доступного пакета – вся программа, то есть любой класс может использовать доступный пакет. Однако необходимо помнить, что обращаться к пакету можно только по его полному составному имени. К пакету java.lang ни из какого места нельзя обратиться как к просто lang.
    Областью видимости импортированного типа являются все объявления верхнего уровня в этом модуле компиляции.
    Областью видимости типа (класса или интерфейса) верхнего уровня является пакет, в котором он объявлен. Из других пакетов доступ возможен либо по составному имени, либо с помощью импортирующего выражения, которое помогает компилятору воссоздать составное имя.
    Область видимости элементов классов или интерфейсов – это все тело типа, в котором они объявлены. Если обращение к этим элементам происходит из другого типа, необходимо воспользоваться составным именем. Имя может быть составлено из простого или составного имени типа, имени объектной переменной или ключевых слов super или this, после чего через точку указывается простое имя элемента.
    Аргументы метода, конструктора или обработчика ошибок видны только внутри этих конструкций и не могут быть доступны извне.
    Область видимости локальных переменных начинается с момента их инициализации и до конца блока, в котором они объявлены. В отличие от полей типов, локальные переменные не имеют значений по умолчанию и должны инициализироваться явно.
    int x; for (int i=0; i<10; i++) { int t=5+i; } // здесь переменная t уже недоступна, // так как блок, в котором она была // объявлена, уже завершен, а переменная // x еще недоступна, так как пока не была // инициализирована
    Определенные проблемы возникают, когда происходит перекрытие областей видимости и возникает конфликт имен различных конструкций языка.

    Область видимости (введение)

    Чтобы не заставлять программистов, совместно работающих над различными классами одной системы, координировать имена, которые они дают различным конструкциям языка, у каждого имени есть область видимости (scope). Если обращение, например, к полю, идет из части кода, попадающей в область видимости его имени, то можно пользоваться простым именем, если нет – необходимо применять составное.
    Например:
    class Point { int x,y;
    int getX() { return x; // простое имя } } class Test { void main() { Point p = new Point(); p.x=3; // составное имя } }
    Видно, что к полю x изнутри класса можно обращаться по простому имени. К нему же из другого класса можно обратиться только по составному имени. Оно составляется из имени переменной, ссылающейся на объект, и имени поля.
    Теперь необходимо рассмотреть области видимости для всех элементов языка. Однако прежде выясним, что такое пакеты, как и для чего они используются.

    Описание исходного текста

    Назад Вперед
    В нашем аплете мы будем создавать объект класса Vector, который является массивом с динамически изменяемым размером. Здесь мы будем хранить координаты нарисованных линий.
    Класс Vector имеет полное имя java.util.Vector, поэтому мы подключаем соответствующую библиотеку классов:
    import java.util.*;

    Отпускание клавиши мыши

    При отпускании клавиши мыши управление получает метод mouseUp:
    public boolean mouseUp(Event evt, int x, int y);
    Анализируя параметры x и y, вы можете определить координаты точки, в которой пользователь отпустил клавишу мыши.

    Пакеты

    Программа на Java представляет собой набор пакетов (packages). Каждый пакет может включать вложенные пакеты, то есть они образуют иерархическую систему.
    Кроме того, пакеты могут содержать классы и интерфейсы и таким образом группируют типы. Это необходимо сразу для нескольких целей. Во-первых, чисто физически невозможно работать с большим количеством классов, если они "свалены в кучу". Во-вторых, модульная декомпозиция облегчает проектирование системы. К тому же, как будет показано ниже, существует специальный уровень доступа, позволяющий типам из одного пакета более тесно взаимодействовать друг с другом, чем с классами из других пакетов. Таким образом, с помощью пакетов производится логическая группировка типов. Из ООП известно, что большая связность системы, то есть среднее количество классов, с которыми взаимодействует каждый класс, заметно усложняет развитие и поддержку такой системы. Используя пакеты, гораздо проще организовать эффективное взаимодействие подсистем друг с другом.
    Наконец, каждый пакет имеет свое пространство имен, что позволяет создавать одноименные классы в различных пакетах. Таким образом, разработчикам не приходится тратить время на разрешение конфликта имен.

    Перемещение курсора мыши

    Когда пользователь перемещает курсор мыши над окном аплета, в процессе перемещения происходит вызов метода mouseMove:
    public boolean mouseMove(Event evt, int x, int y);
    Через переменные x и y передаются текущие координаты курсора мыши.

    Платформенная поддержка пакетов

    Простейшим способом организации пакетов и типов является обычная файловая структура. Рассмотрим вырожденный пример, когда все пакеты, исходный и бинарный код располагаются в одном каталоге и его подкаталогах.
    В этом корневом каталоге должна быть папка java, соответствующая основному пакету языка, а в ней, в свою очередь, вложенные папки applet, awt, io, lang, net, util.
    Предположим, разработчик работает над моделью солнечной системы, для чего создал классы Sun, Moon и Test и расположил их в пакете space.sunsystem. В таком случае в корневом каталоге должна быть папка space, соответствующая одноименному пакету, а в ней – папка sunsystem, в которой хранятся классы этого разработчика.
    Как известно, исходный код располагается в файлах с расширением .java, а бинарный – с расширением .class. Таким образом, содержимое папки sunsystem может выглядеть следующим образом:
    Moon.java Moon.class Sun.java Sun.class Test.java Test.class
    Другими словами, исходный код классов
    space.sunsystem.Moon space.sunsystem.Sun space.sunsystem.Test
    хранится в файлах
    space\sunsystem\Moon.java space\sunsystem\Sun.java space\sunsystem\Test.java
    а бинарный код – в соответствующих .class-файлах. Обратите внимание, что преобразование имен пакетов в файловые пути потребовало замены разделителя . (точки) на символ-разделитель файлов (для Windows это обратный слэш \). Такое преобразование может выполнить как компилятор для поиска исходных текстов и бинарного кода, так и виртуальная машина для загрузки классов и интерфейсов.
    Обратите внимание, что было бы ошибкой запускать Java прямо из папки space\sunsystem и пытаться обращаться к классу Test, несмотря на то, что файл-описание лежит именно в ней. Необходимо подняться на два уровня каталогов выше, чтобы Java, построив путь из имени пакета, смогла обнаружить нужный файл.
    Кроме того, немаловажно, что Java всегда различает регистр идентификаторов, а значит, названия файлов и каталогов должны точно отвечать запрограммированным именам. Хотя в некоторых случаях операционная система может обеспечить доступ, невзирая на регистр, при изменении обстоятельств расхождения могут привести к сбоям.

    Существует специальное выражение, объявляющее пакет (подробно рассматривается ниже). Оно предшествует объявлению типа и обозначает, какому пакету будет принадлежать этот тип. Таким образом, набор доступных пакетов определяется набором доступных файлов, содержащих объявления типов и пакетов. Например, если создать пустой каталог, или заполнить его посторонними файлами, это отнюдь не приведет к появлению пакета в Java.

    Какие файлы доступны для утилит Java SDK (компилятора, интерпретатора и т.д.), устанавливается на уровне операционной системы, ведь утилиты – это обычные программы, которые выполняются под управлением ОС и, конечно, следуют ее правилам. Например, если пакет содержит один тип, но описывающий его файл недоступен текущему пользователю ОС для чтения, для Java этот тип и этот пакет не будут существовать.

    Понятно, что далеко не всегда удобно хранить все файлы в одном каталоге. Зачастую классы находятся в разных местах, а некоторые могут даже распространяться в виде архивов, для ускорения загрузки через сеть. Копировать все такие файлы в одну папку было бы крайне затруднительно.

    Поэтому Java использует специальную переменную окружения, которая называется classpath. Аналогично тому, как переменная path помогает системе находить и загружать динамические библиотеки, эта переменная помогает работать с Java-классами. Ее значение должно состоять из путей к каталогам или архивам, разделенных точкой с запятой. С версии 1.1 поддерживаются архивы типов ZIP и JAR (Java ARchive) – специальный формат, разработанный на основе ZIP для Java.

    Например, переменная classpath может иметь такое значение:

    .;c:\java\classes;d:\lib\3Dengine.zip; d:\lib\fire.jar

    В результате все указанные каталоги и содержимое всех архивов "добавляется" к исходному корневому каталогу. Java в поисках класса будет искать его по описанному выше правилу во всех указанных папках и архивах по порядку. Обратите внимание, что первым в переменной указан текущий каталог (представлен точкой). Это делается для того, чтобы поиск всегда начинался с исходного корневого каталога. Конечно, такая запись не является обязательной и делается на усмотрение разработчика.

    Несмотря на явные удобства такой конструкции, она таит в себе и опасности. Если разрабатываемые классы хранятся в некотором каталоге и он указан в classpath позже, чем некий другой каталог, в котором обнаруживаются одноименные типы, разобраться в такой ситуации будет непросто. В классы будут вноситься изменения, которые никак не проявляются при запуске из-за того, что Java на самом деле загружает одни и те же файлы из посторонней папки.

    Поэтому к данной переменной среды окружения необходимо относиться с особым вниманием. Полезно помнить, что необязательно устанавливать ее значение сразу для всей операционной системы. Его можно явно указывать при каждом запуске компилятора или виртуальной машины как опцию, что, во-первых, никогда не повлияет на другие Java-программы, а во-вторых, заметно упрощает поиск ошибок, связанных с некорректным значением classpath.

    Наконец, можно применять и альтернативные подходы к хранению пакетов и файлов с исходным и бинарным кодом. Например, в качестве такого хранилища может использоваться база данных. Более того, существует ограничение на размещение объявлений классов в .java-файлах, которое рассматривается ниже, а при использовании БД любые ограничения можно снять. Тем не менее, при таком подходе рекомендуется предоставлять утилиты импорта/экспорта с учетом ограничения для преобразований из/в файлы.

    Поля класса LineDraw

    В нашем классе мы определили несколько полей, предназначенных для хранения текущих координат рисуемых линий:
    Dimension dmDown; Dimension dmUp; Dimension dmPrev; boolean bDrawing; Vector lines;
    В переменную dmDown класса Dimension записываются координаты курсора на момент нажатия клавиши мыши. Если пользователь нажал клавишу мыши для того чтобы приступить к рисованию линии, это будет координатами начала линии.
    Когда пользователь отпускает клавишу мыши, координаты записываются в переменную dmUp.
    В процессе рисования линии метод mouseDrag стирает ранее нарисованную линию и рисует новую. Координаты конца старой линии хранятся в переменной dmPrev.
    Переменная bDrawing типа boolean хранит текущее состояние аплета. Когда аплет находится в состоянии рисования линии, в эту переменную записывается значение true, а когда нет - значение false.
    И, наконец, переменная lines типа Vector является динамическим массивом, в котором хранятся координаты нарисованных линий.

    Простые и составные имена. Элементы

    Имена бывают простыми (simple), состоящими из одного идентификатора (они определяются во время объявления) и составными (qualified), состоящими из последовательности идентификаторов, разделенных точкой. Для пояснения этих терминов необходимо рассмотреть еще одно понятие.
    У пакетов и ссылочных типов (классов, интерфейсов, массивов) есть элементы (members). Доступ к элементам осуществляется с помощью выражения, состоящего из имен, например, пакета и класса, разделенных точкой.
    Далее классы и интерфейсы будут называться объединяющим термином тип (type).
    Элементами пакета являются содержащиеся в нем классы и интерфейсы, а также вложенные пакеты. Чтобы получить составное имя пакета, необходимо к полному имени пакета, в котором он располагается, добавить точку, а затем его собственное простое имя. Например, составное имя основного пакета языка Java – java.lang (то есть простое имя этого пакета lang, и он находится в объемлющем пакете java). Внутри него есть вложенный пакет, предназначенный для типов технологии reflection, которая упоминалась в предыдущих главах. Простое название пакета reflect, а значит, составное – java.lang.reflect.
    Простое имя классов и интерфейсов дается при объявлении, например, Object, String, Point. Чтобы получить составное имя таких типов, надо к составному имени пакета, в котором находится тип, через точку добавить простое имя типа. Например, java.lang.Object, java.lang.reflect.Method или com.myfirm.MainClass. Смысл последнего выражения таков: сначала идет обращение к пакету com, затем к его элементу – вложенному пакету myfirm , а затем к элементу пакета myfirm – классу MainClass. Здесь com.myfirm – составное имя пакета, где лежит класс MainClass, а MainClass — простое имя. Составляем их и разделяем точкой – получается полное имя класса com.myfirm.MainClass.
    Для ссылочных типов элементами являются поля и методы, а также внутренние типы (классы и интерфейсы). Элементы могут быть как непосредственно объявлены в классе, так и получены по наследству от родительских классов и интерфейсов, если таковые имеются. Простое имя элементов также дается при инициализации. Например, toString(), PI, InnerClass. Составное имя получается путем объединения простого или составного имени типа, или переменной объектного типа с именем элемента. Например, ref.toString(), java.lang.Math.PI, OuterClass.InnerClass. Другие обращения к элементам ссылочных типов уже неоднократно применялись в предыдущих главах.

    События от мыши

    Назад Вперед
    В этом разделе мы рассмотрим события, которые возникают в результате того, что пользователь выполняет в окне аплета операции с мышью. Это такие операции, как нажатие и отпускание клавиши мыши, перемещение курсора мыши в окне аплета с нажатой или отпущенной клавишей, перемещение курсора мыши в окно аплета и удаление этого курсора из окна аплета.
    Все перечисленные ниже методы должны вернуть значение true, если обработка события выполнена успешно и дальнейшая обработка не требуется. Если же методы вернут значение false, событие будет обработано методом из базового класса, то есть для него будет выполнена обработка, принятая по умолчанию.
    Программисты, создававшие приложения для операционной системы Microsoft Windows, могут найти здесь аналогию с вызовом функции DefWindowProc, которая выполняет обработку сообщений, принятую по умолчанию.

    От аплетов Java было бы

    Назад Вперед
    От аплетов Java было бы немного толку, если бы они не умели обрабатывать информацию, поступающую от мыши и клавиатуры. К счастью, такая обработка предусмотрена и она выполняется достаточно просто.
    Когда пользователь выполняет операции с мышью или клавиатурой в окне аплета, возникают события, которые передаются соответствующим методам класса Applet. Переопределяя эти методы, вы можете организовать обработку событий, возникающих от мыши или клавиатуры.
    Если вы создавали приложения для операционной системы Microsoft Windows, здесь для вас нет ничего нового - вспомните, как вы обрабатывали сообщение WM_LBUTTONDOWN или WM_CHAR. Когда пользователь выполнял действие с мышью или клавиатурой в окне приложения, функция этого окна получала соответствующее сообщение. Методы класса Applet, обрабатывающие события от мыши и клавиатуры, являются аналогами обработчиков указанных сообщений.
    Заметим, что аплеты имеют дело только с левой клавишей мыши. В текущей версии Java вы не можете никаким образом задействовать в аплете правую или среднюю клавишу мыши.
    Назад Вперед

    От аплетов Java было бы От аплетов Java было бы Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    От аплетов Java было бы
    printmenus();
    От аплетов Java было бы Программные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все От аплетов Java было бы »


    От аплетов Java было бы Solaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все От аплетов Java было бы »


    От аплетов Java было бы Каталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все От аплетов Java было бы »


    От аплетов Java было бы Гарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все От аплетов Java было бы »


    От аплетов Java было бы Описание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все От аплетов Java было бы »


    От аплетов Java было бы Проекты
    События
    Lab Downloads
    Посмотреть все От аплетов Java было бы »


    От аплетов Java было бы

    Соглашения по именованию

    Для того, чтобы код, написанный на Java, было легко читать и понять не только его автору, но и другим разработчикам, а также для устранения некоторых конфликтов имен, предлагаются следующие соглашения по именованию элементов языка Java. Стандартные библиотеки и классы Java также следуют им там, где это возможно.
    Соглашения регулируют именование следующих конструкций:
  • пакеты;
  • типы (классы и интерфейсы);
  • методы;
  • поля;
  • поля-константы;
  • локальные переменные и параметры методов и др.

  • Рассмотрим их последовательно.
    Правила построения имен пакетов уже подробно рассматривались в этой главе. Имя каждого пакета начинается с маленькой буквы и представляет собой, как правило, одно недлинное слово. Если требуется составить название из нескольких слов, можно воспользоваться знаком подчеркивания или начинать следующее слово с большой буквы. Имя пакета верхнего уровня обычно соответствует доменному имени первого уровня. Названия java и javax (Java eXtension) зарезервированы компанией Sun для стандартных пакетов Java.
    При возникновении ситуации "заслоняющего" объявления (obscuring) можно изменить имя локальной переменной, что не повлечет за собой глобальных изменений в коде. Случай же конфликта с именем типа не должен возникать, согласно правилам именования типов.
    Имена типов начинаются с большой буквы и могут состоять из нескольких слов, каждое следующее слово также начинается с большой буквы. Конечно, надо стремиться к тому, чтобы имена были описательными, "говорящими".
    Имена классов, как правило, являются существительными:
    Human HighGreenOak ArrayIndexOutOfBoundsException
    (Последний пример – ошибка, возникающая при использовании индекса массива, который выходит за границы допустимого.)
    Аналогично задаются имена интерфейсов, хотя они не обязательно должны быть существительными. Часто используется английский суффикс "able":
    Runnable Serializable Cloneable
    Проблема "заслоняющего" объявления (obscuring) для типов встречается редко, так как имена пакетов и локальных переменных (параметров) начинаются с маленькой буквы, а типов – с большой.
    Имена методов должны быть глаголами и обозначать действия, которые совершает данный метод. Имя должно начинаться с маленькой буквы, но может состоять из нескольких слов, причем каждое следующее слово начинается с заглавной буквы. Существует ряд принятых названий для методов:
  • если методы предназначены для чтения и изменения значения переменной, то их имена начинаются, соответственно, с get и set, например, для переменной size это будут getSize() и setSize();
  • метод, возвращающий длину, называется length(), например, в классе String;
  • имя метода, который проверяет булевское условие, начинается с is, например, isVisible() у компонента графического пользовательского интерфейса;
  • метод, который преобразует величину в формат F, называется toF(), например, метод toString(), который приводит любой объект к строке.


  • Вообще, рекомендуется везде, где возможно, называть методы похожим образом, как в стандартных классах Java, чтобы они были понятны всем разработчикам.

    Поля класса имеют имена, записываемые в том же стиле, что и для методов, начинаются с маленькой буквы, могут состоять из нескольких слов, каждое следующее слово начинается с заглавной буквы. Имена должны быть существительными, например, поле name в классе Human, или size в классе Planet.

    Как для полей решается проблема "заслоняющего" объявления (obscuring), уже обсуждалось.

    Поля могут быть константами, если в их объявлении стоит ключевое слово final. Их имена состоят из последовательности слов, сокращений, аббревиатур. Записываются они только большими буквами, слова разделяются знаками подчеркивания:

    PI MIN_VALUE MAX_VALUE

    Иногда константы образуют группу, тогда рекомендуется использовать одно или несколько одинаковых слов в начале имен:

    COLOR_RED COLOR_GREEN COLOR_BLUE

    Наконец, рассмотрим имена локальных переменных и параметров методов, конструкторов и обработчиков ошибок. Они, как правило, довольно короткие, но, тем не менее, должны быть осмыслены. Например, можно использовать аббревиатуру (имя cp для ссылки на экземпляр класса ColorPoint) или сокращение (buf для buffer).

    Распространенные однобуквенные сокращения:

    byte b; char c; int i,j,k; long l; float f; double d; Object o; String s; Exception e; // объект, представляющий // ошибку в Java

    Двух- и трехбуквенные имена не должны совпадать с принятыми доменными именами первого уровня Internet-сайтов.

    Уникальность имен пакетов

    Поскольку Java создавался как язык, предназначенный для распространения приложений через Internet, а приложения состоят из структуры пакетов, необходимо предпринять некоторые усилия, чтобы не произошел конфликт имен. Имена двух используемых пакетов могут совпасть по прошествии значительного времени после их создания. Исправить такое положение обычному программисту будет крайне затруднительно.
    Поэтому создатели Java предлагают следующий способ уникального именования пакетов. Если программа создается разработчиком, у которого есть Internet-сайт, либо же он работает на организацию, у которой имеется сайт, и доменное имя такого сайта, например, company.com, то имена пакетов должны начинаться с этих же слов, выписанных в обратном порядке: com.company. Дальнейшие вложенные пакеты могут носить названия подразделений компании, пакетов, фамилий разработчиков, имена компьютеров и т.д.
    Таким образом, пакет верхнего уровня всегда записывается ASCII-буквами в нижнем регистре и может иметь одно из следующих имен:
  • трехбуквенные com, edu, gov, mil, net, org, int (этот список расширяется);
  • двухбуквенные, обозначающие имена стран, такие как ru, su, de, uk и другие.

  • Если имя сайта противоречит требованиям к идентификаторам Java, то можно предпринять следующие шаги:
  • если в имени стоит запрещенный символ, например, тире, то его можно заменить знаком подчеркивания;
  • если имя совпадает с зарезервированным словом, можно в конце добавить знак подчеркивания;
  • если имя начинается с цифры, можно в начале добавить знак подчеркивания.

  • Примеры имен пакетов, составленных по таким правилам:
    com.sun.image.codec.jpeg org.omg.CORBA.ORBPackage oracle.jdbc.driver.OracleDriver
    Однако, конечно, никто не требует, чтобы Java-пакеты были обязательно доступны на Internet-сайте, который дал им имя. Скорее была сделана попытка воспользоваться существующей системой имен вместо того, чтобы создавать новую для именования библиотек.

    Вход курсора мыши в область окна аплета

    Метод mouseEnter получает управление, когда курсор мыши в процессе перемещения по экрану попадает в область окна аплета:
    public boolean mouseEnter(Event evt, int x, int y);
    Вы можете использовать этот метод для активизации аплета, на который указывает курсор мыши.

    а не как термины ООП,

    Имена (names) используются в программе для доступа к объявленным (declared) ранее "объектам", "элементам", "конструкциям" языка (все эти слова-синонимы были использованы здесь в их общем смысле, а не как термины ООП, например). Конкретнее, в Java имеются имена:

  • пакеты
  • классы;
  • интерфейсы;

  • элементы (member) ссылочных типов:
  • поля;
  • методы;
  • внутренние классы и интерфейсы;
  • аргументы:
  • методов;
  • конструкторов;
  • обработчиков ошибок;
  • локальные переменные.

  • Соответственно, все они должны быть объявлены специальным образом, что будет постепенно рассматриваться по ходу курса. Так же объявляются конструкторы, однако их имя совпадает с именем класса, поэтому они не попали в этот список.
    Напомним, что пакеты (packages) в Java – это способ логически группировать классы, что необходимо, поскольку зачастую количество классов в системе составляет несколько тысяч, или даже десятков тысяч. Кроме классов и интерфейсов в пакетах, могут находиться вложенные пакеты. Синонимами этого слова в других языках являются библиотека или модуль.

    Выход курсора мыши из области окна аплета

    Метод mouseExit вызывается при покидании куросром окна аплета:
    public boolean mouseExit(Event evt, int x, int y);
    Если пользователь убрал курсор из окна аплета, активизированного методом mouseEnter, то метод mouseExit может переключить аплет в пассивное состояние.
    Назад Вперед

    Выход курсора мыши из области окна аплетаВыход курсора мыши из области окна аплета Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Выход курсора мыши из области окна аплета
    printmenus();
    Выход курсора мыши из области окна аплетаПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Выход курсора мыши из области окна аплета»


    Выход курсора мыши из области окна аплетаSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Выход курсора мыши из области окна аплета»


    Выход курсора мыши из области окна аплетаКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Выход курсора мыши из области окна аплета»


    Выход курсора мыши из области окна аплетаГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Выход курсора мыши из области окна аплета»


    Выход курсора мыши из области окна аплетаОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Выход курсора мыши из области окна аплета»


    Выход курсора мыши из области окна аплетаПроекты
    События
    Lab Downloads
    Посмотреть все Выход курсора мыши из области окна аплета»


    Выход курсора мыши из области окна аплета

    Выполнение операции Drag and Drop

    Операция Drag and Drop выполняется следующим образом: пользователь нажимает клавишу мыши и, не отпуская ее, начинает перемещать курсор мыши. При этом происходит вызов метода mouseDrag:
    public boolean mouseDrag(Event evt, int x, int y);
    Через переменные x и y передаются текущие координаты курсора мыши. Метод mouseDrag вызывается даже в том случае, если в процессе перемещения курсор вышел за пределы окна аплета.

    В этой главе был рассмотрен

    В этой главе был рассмотрен механизм именования элементов языка. Для того, чтобы различные части большой системы не зависели друг от друга, вводится понятие "область видимости имени", вне которой необходимо использовать не простое, а составное имя. Затем были изучены элементы (members), которые могут быть у пакетов и ссылочных типов. Также рассматривалась связь терминов "идентификатор" (из темы "Лексика") и имя.
    Затем были рассмотрены пакеты, которые используются в Java для создания физической и логической структуры классов, а также для более точного разграничения области видимости. Пакет содержит вложенные пакеты и типы (классы и интерфейсы). Вопрос о платформенной поддержке пакетов привел к рассмотрению модулей компиляции как текстовых файлов, поскольку именно в виде файлов и каталогов, как правило, хранятся и распространяются Java-приложения. Тогда же впервые был рассмотрен вопрос разграничения доступа, так как доступ к модулям компиляции определяется именно платформенной поддержкой, а точнее – операционной системой.
    Модуль компиляции состоит из трех основных частей – объявление пакета, импорт-выражения и объявления верхнего уровня. Важную роль играет безымянный пакет, или пакет по умолчанию, хотя он и не рекомендуется для применения при создании больших систем. Были изучены детали применения двух видов импорт-выражений – импорт класса и импорт пакета. Наконец, было начато рассмотрение объявлений верхнего уровня (эта тема будет продолжена в главе, описывающей объявление классов). Пакеты, как и другие элементы языка, имеют определенные соглашения по именованию, призванные облегчить понимание кода и уменьшить возможность возникновения ошибок и двусмысленных ситуаций в программе.
    Описание области видимости для различных элементов языка приводит к вопросу о возможных перекрытиях таких областей и, как следствие, о конфликтах имен. Рассматриваются "затеняющие" и "заслоняющие" объявления. Для устранения или уменьшения возможности возникновения таких ситуаций описываются соглашения по именованию для всех элементов языка.

    "Заслоняющее" объявление (Obscuring)

    Может возникнуть ситуация, когда простое имя может быть одновременно рассмотрено как имя переменной, типа или пакета.
    Приведем пример, который частично иллюстрирует такой случай:
    import java.awt.*;
    public class Obscuring { static Point Test = new Point(3,2); public static void main (String s[]) { print(Test.x); } } class Test { static int x = -5; }
    В методе main() простое имя Test одновременно обозначает имя поля класса Obscuring и имя другого типа, находящегося в том же пакете,– Test. С помощью этого имени происходит обращение к полю x, которое определено и в классе java.awt.Point и Test.
    Результатом этого примера станет 3, то есть переменная имеет более высокий приоритет. В свою очередь, тип имеет более высокий приоритет, чем пакет. Таким образом, обращение к доступному в обычных условиях типу или пакету может оказаться невозможным, если есть объявление одноименной переменной или типа, имеющее более высокий приоритет. Такое объявление называется "заслоняющим" (obscuring).
    Эта проблема скорее всего не возникнет, если следовать соглашениям по именованию элементов языка Java.

    "Затеняющее" объявление (Shadowing)

    Самыми распространенными случаями возникновения конфликта имен являются выражение, импортирующее пакет, и объявление локальных переменных, или параметров методов, конструкторов, обработчиков ошибок. Импорт пакета подробно рассматривался в этой главе. Если импортированный и текущий пакеты содержат одноименные типы, то их области пересекаются. Как уже говорилось, предпочтение отдается типу из текущего пакета. Также рассказывалось о том, как эту проблему решать.
    Перейдем к проблеме перекрытия имен полей класса и локальных переменных. Пример:
    class Human { int age; // возраст int getAge() { return age; } void setAge(int age) { age=age; // ??? } }
    В классе Human (человек) объявлено поле age (возраст). Удобно определить также метод setAge(), который должен устанавливать новое значение возраста для человека. Вполне логично сделать у метода setAge() один входной аргумент, который также будет называть age (ведь в качестве этого аргумента будет передаваться новое значение возраста). Получается, что в реализации метода setAge() нужно написать age=age, в первом случае подразумевая поле класса, во втором - параметр метода. Понятно, что хотя с точки зрения компилятора это корректная конструкция, попытка сослаться на две разные переменные через одно имя успехом не увенчается. Надо заметить, что такие ошибки случаются порой даже у опытных разработчиков.
    Во-первых, рассмотрим, из-за чего возникла конфликтная ситуация. Есть два элемента языка – аргумент метода и поле класса, области видимости которых пересеклись. Область видимости поля класса больше, она охватывает все тело класса, в то время как область видимости аргумента метода включает только сам метод. В таком случае внутри области пересечения по простому имени доступен именно аргумент метода, а поле класса "затеняется" (shadowing) объявлением параметра метода.
    Остается вопрос, как в такой ситуации все же обратиться к полю класса. Если доступ по простому имени невозможен, надо воспользоваться составным. Здесь удобнее всего применить специальное ключевое слово this (оно будет подробно рассматриваться в следующих главах). Слово this имеет значение ссылки на объект, внутри которого оно применяется. Если вызвать метод setAge() у объекта класса Human и использовать в этом методе слово this, то его значение будет ссылкой на данный объект.
    Исправленный вариант примера:
    class Human { int age; // возраст
    void setAge(int age) { this.age=age; // верное присвоение! } }
    Конфликт имен, возникающий из-за затеняющего объявления, довольно легко исправить с помощью ключевого слова this или других конструкций языка, в зависимости от обстоятельств. Наибольшей проблемой является то, что компилятор никак не сообщает о таких ситуациях, и самое сложное – выявить ее с помощью тестирования или контрольного просмотра кода.

    Программирование на Java

    Аплет FormDemo

    Назад Вперед
    В аплете FormDemo мы покажем приемы работы с компонентами, такими как переключатели, кнопки, текстовые поля и списки.
    Мы разместили несколько таких компонент в окне этого аплета (рис. 7) таким образом, что они образуют собой форму. В этой форме вы можете ввести имя и фамилию, выбрать один из трех режимов работы, а также цвет.

    Аплет FormDemo Рис. 7. Окно аплета FormDemo
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    Переключатели First и Second активизируют однострочные текстовые поля редактирования Enter your first name и Enter your second name. После того как пользователь нажмет кнопку Ready, содержимое активных полей, а также состояние переключателей Mode 1, Mode 2 и Mode 3 будет отображено в многострочном поле редактирования. Это поле находится в нижней части окна аплета.
    С помощью списка, расположенного справа от переключателя Mode 3, можно задавать цвет фона многострочного поля. Цвет устанавливается сразу после выбора новой строки из этого списка.
    К сожалению, при изменении размеров окна аплета находящиеся в нем компоненты изменяют свое расположение. Этот недостаток мы устраним после того, как расскажем вам о системе Layout Manager, с помощью которой вы можете управлять размещением компонент в окне аплета.

    Дополнительные свойства классов

    Рассмотрим в этом разделе некоторые особенности работы с классами в Java. Обсуждение данного вопроса будет продолжено в специальной лекции, посвященной объектной модели в Java.

    Инициализаторы

    Наконец, последней допустимой конструкцией в теле класса является объявление инициализаторов. Записываются объектные инициализаторы очень просто – внутри фигурных скобок.
    public class Test { private int x, y, z;
    // инициализатор объекта { x=3; if (x>0) y=4; z=Math.max(x, y); } }
    Инициализаторы не имеют имен, исполняются при создании объектов, не могут быть вызваны явно, не передаются по наследству (хотя, конечно, инициализаторы в родительском классе продолжают исполняться при создании объекта класса-наследника).
    Было указано уже три вида инициализирующего кода в классах – конструкторы, инициализаторы переменных, а теперь добавились объектные инициализаторы. Необходимо разобраться, в какой последовательности что выполняется, в том числе при наследовании. При создании экземпляра класса вызванный конструктор выполняется следующим образом:
  • если первой строкой идет обращение к конструктору родительского класса (явное или добавленное компилятором по умолчанию), то этот конструктор исполняется;
  • в случае успешного исполнения вызываются все инициализаторы полей и объекта в том порядке, в каком они объявлены в теле класса;
  • если первой строкой идет обращение к другому конструктору этого же класса, то он вызывается. Повторное выполнение инициализаторов не производится.

  • Второй пункт имеет ряд важных следствий. Во-первых, из него следует, что в инициализаторах нельзя использовать переменные класса, если их объявление записано позже.
    Во-вторых, теперь можно сформулировать наиболее гибкий подход к инициализации final-полей. Главное требование – чтобы такие поля были проинициализированы ровно один раз. Это можно обеспечить в следующих случаях:
  • если инициализировать поле при объявлении;
  • если инициализировать поле только один раз в инициализаторе объекта (он должен быть записан после объявления поля);
  • если инициализировать поле только один раз в каждом конструкторе, в первой строке которого стоит явное или неявное обращение к конструктору родителя. Конструктор, в первой строке которого стоит this, не может и не должен инициализировать final-поле, так как цепочка this-вызовов приведет к конструктору с super, в котором эта инициализация обязательно присутствует.


  • Для иллюстрации порядка исполнения инициализирующих конструкций рассмотрим следующий пример:

    public class Test { { System.out.println("initializer"); } int x, y=getY(); final int z; { System.out.println("initializer2"); } private int getY() { System.out.println("getY() "+z); return z; } public Test() { System.out.println("Test()"); z=3; } public Test(int x) { this(); System.out.println("Test(int)"); // z=4; - нельзя! final-поле уже // было инициализировано } }

    После выполнения выражения new Test() на консоли появится:

    initializer getY() 0 initializer2 Test()

    Обратите внимание, что для инициализации поля y вызывается метод getY(), который возвращает значение final-поля z, которое еще не было инициализировано. Поэтому в итоге поле y получит значение по умолчанию 0, а затем поле z получит постоянное значение 3, которое никогда уже не изменится.

    После выполнения выражения new Test(3) на консоли появится:

    initializer getY() 0 initializer2 Test() Test(int)

    Исходный текст аплета FormDemo

    Исходный текст аплета FormDemo вы найдете в листинге 1.
    Листинг 1. Файл FormDemo.java
    import java.applet.Applet; import java.awt.*; import java.util.*;
    public class FormDemo extends Applet { Button btReady;
    Checkbox chbox1; Checkbox chbox2;
    CheckboxGroup grRadio; Checkbox rd1; Checkbox rd2; Checkbox rd3;
    Choice ch1;
    Label lbFirstName; Label lbSecondName;
    TextField txtFirstName; TextField txtSecondName; TextArea txta;
    public void init() { chbox1 = new Checkbox("First"); add(chbox1);
    lbFirstName = new Label("Enter your first name:"); add(lbFirstName);
    txtFirstName = new TextField(" ", 30); add(txtFirstName);
    chbox2 = new Checkbox("Second"); add(chbox2);
    lbSecondName = new Label("Enter your second name:"); add(lbSecondName);
    txtSecondName = new TextField(" ", 30); add(txtSecondName);
    grRadio = new CheckboxGroup(); rd1 = new Checkbox("Mode 1", grRadio, true); rd2 = new Checkbox("Mode 2", grRadio, false); rd3 = new Checkbox("Mode 3", grRadio, false);
    add(rd1); add(rd2); add(rd3);
    ch1 = new Choice(); ch1.addItem("White"); ch1.addItem("Green"); ch1.addItem("Yellow");
    add(ch1);
    setBackground(Color.yellow);
    lbFirstName.setBackground(Color.yellow); lbSecondName.setBackground(Color.yellow);
    rd1.setBackground(Color.yellow); rd2.setBackground(Color.yellow); rd3.setBackground(Color.yellow);
    chbox1.setBackground(Color.yellow); chbox2.setBackground(Color.yellow);
    txta = new TextArea("", 6, 45); add(txta); txta.setBackground(Color.white);
    btReady = new Button("Ready"); add(btReady); }
    public String getAppletInfo() { return "Name: FormDemo"; }
    public void paint(Graphics g) { Dimension dimAppWndDimension = getSize();
    g.setColor(Color.black); g.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1); }
    public boolean action(Event evt, Object obj) { Button btn; String str1, str2;
    if(evt.target instanceof Button) { if(evt.target.equals(btReady)) { btn = (Button)evt.target;

    Класс Checkbox

    Переключатели с независимой и зависимой фиксацией создаются на базе класса Checkbox. Приведем прототипы конструкторов и методов этого класса:

    Класс Choice

    Приведем описание прототипов конструктора и методов класса Choice:

    Кнопки

    Назад Вперед
    Как мы уже говорили, стандартные кнопки создаются на базе класса Button. Этот класс очень простой, поэтому мы приведем полное его описание:

    Компоненты в окне аплета

    Назад Вперед
    Практически каждое приложение Windows, за исключением самых простейших, имеет такие органы управления, как меню, кнопки, поля редактирования текстовой информации, переключатели с независимой и зависимой фиксацией и списки. Кроме того, приложение Windows может создавать диалоговые панели, содержащие перечисленные выше и другие органы управления.
    В окне аплета вы также можете разместить некоторые из перечисленных выше органов управления, а именно:
  • кнопки;

  • переключатели с независимой фиксацией;

  • переключатели с зависимой фиксацией;

  • статические текстовые поля;

  • однострочные и многострочные поля редактирования текста;

  • списки;

  • полосы просмотра

  • Самый большой и едва ли приятный сюрприз для вас это то, что при размещении перечисленных органов управления в окне аплета вы не можете задать для них точные координаты и размеры. Размещением занимается система управления внешним видом Layout Manager, которая располагает органы управления по-своему. Вы, однако, можете задавать несколько режимов размещения (последовательное, в ячейках таблицы и так далее), но не координаты или размеры. Это сделано для обеспечения независимости приложений Java от платформ, на которых они выполняются.
    Органы управления создаются как объекты классов, порожденных от класса Component (рис. 1). Поэтому в дальнейшем мы будем называть органы управления компонентами.
    Компоненты в окне аплета
    Рис. 1. Взаимосвязь классов органов управления в приложениях Java
    Класс Button позволяет создавать стандартные кнопки. Если вам нужна нестандартная кнопка (например, графическая кнопка), вы можете создать ее на базе класса Canvas.
    Для создания переключателей с независимой или зависимой фиксацией предназначен класс CheckBox.
    С помощью класса Label вы можете создавать в окне аплета текстовые строки, например, надписи для других компонент. Эти строки не редактируются пользователем.
    Класс List, как нетрудно догадаться из названия, предназначен для создания списков.
    С помощью класса Scrollbar вы можете создавать полосы просмотра, которые используются, в частности, могострочными полями редактирования текста.

    Метод action

    В методе action мы определили рабочие поля btn, str1 и str2:
    Button btn; String str1, str2;
    В начале своей работы метод action определяет, какой компонент вызвал событие. Для этого анализируется поле evt.target:
    if(evt.target instanceof Button) { . . . return true; } else if(evt.target instanceof Choice) { . . . return true; } return false;
    Наш метод action обрабатывает события, вызываемые объектами классов Button и Choice. Если событие вызвано компонентом, относящимся к какому-либо другому классу, метод возвращает значение false. Этим он сигнализирует, что обработка события не выполнялась.
    В случае успешной обработки события метод action возвращает значение true.
    Если событие вызвано кнопкой, наш метод action проверяет, какой именно. Обработка выполняется только в том случае, если через поле evt.target передается ссылка на кнопку btReady:
    if(evt.target.equals(btReady)) { . . . } else { return false; } return true;
    В противном случае метод action возвращает значение false, отказываясь от обработки события.
    Что делает обработчик события, создаваемого кнопкой?
    Прежде всего, он сохраняет ссылку на кнопку в рабочей переменной (просто для того чтобы показать, как это делается):
    btn = (Button)evt.target;
    Далее наш обработчик события извлекает текстовые строки из однострочных текстовых полей, вызывая для этого метод getText. Эти строки записываются в рабочие переменные str1 и str2:
    str1 = txtFirstName.getText(); str2 = txtSecondName.getText();
    Затемметод action проверяет состояние переключателей с независимой фиксацией chbox1 и chbox2. Если они включены, содержимое соответствующих временных переменных добавляется в многострочное текстовое поле txta:
    if(chbox1.getState()) txta.append(str1);
    if(chbox2.getState()) txta.append(str2);
    Для добавления мы вызываем метод append.
    Аналогичным образом преверяется состояние переключателей с зависимой фиксацией:
    if(rd1.getState()) txta.append("\nMode 1\n");
    if(rd2.getState()) txta.append("\nMode 2\n");

    Метод init

    Метод init занимается созданием компонент и добавлением их в окно алпета. Кроме того, этот метод изменяет цвет фона окна аплета и окон добавляемых компонент.
    Прежде всего метод init создает два переключателя с независимой фиксацией, два объекта класса Label и два однострочных поля редактирования текста:
    chbox1 = new Checkbox("First"); add(chbox1);
    lbFirstName = new Label("Enter your first name:"); add(lbFirstName);
    txtFirstName = new TextField(" ", 30); add(txtFirstName);
    chbox2 = new Checkbox("Second"); add(chbox2);
    lbSecondName = new Label("Enter your second name:"); add(lbSecondName);
    txtSecondName = new TextField(" ", 30); add(txtSecondName);
    Поля создаются при помощи конструкторов, а добавляются в окно аплета методом add. Согласно схемы расположения компонент, установленой по умолчанию, добавляемые компоненты размещаются сверху вниз и слева направо.
    Для группы переключателей с зависимой фиксацией мы создаем объект класса CheckboxGroup:
    grRadio = new CheckboxGroup();
    Ссылка на этот объект затем передается в качестве второго параметра конструкторам, создающим переключатели:
    rd1 = new Checkbox("Mode 1", grRadio, true); rd2 = new Checkbox("Mode 2", grRadio, false); rd3 = new Checkbox("Mode 3", grRadio, false);
    Переключатели добавляются в окно аплета при помощи метода add:
    add(rd1); add(rd2); add(rd3);
    Список цветов создается как объект класса Choice:
    ch1 = new Choice();
    После создания списка мы добавляем в него три элемента, вызывая для этого метод addItem:
    ch1.addItem("White"); ch1.addItem("Green"); ch1.addItem("Yellow");
    Вслед за этим мы добавляем сформированный список в окно аплета:
    add(ch1);
    Для установки цвета фона мы вызываем метод setBackground без указания объекта:
    setBackground(Color.yellow);
    В этом случае метод вызывается для текущего объекта, то есть для нашего аплета. Чтобы установить цвет фона в окнах компонент, мы вызываем метод setBackground для соответствующих объектов, как это показано ниже:
    lbFirstName.setBackground(Color.yellow); lbSecondName.setBackground(Color.yellow);
    rd1.setBackground(Color.yellow); rd2.setBackground(Color.yellow); rd3.setBackground(Color.yellow);
    chbox1.setBackground(Color.yellow); chbox2.setBackground(Color.yellow);
    Многострочное текстовое поле создается как объект класса TextArea. В нем 6 строк и 45 столбцов:
    txta = new TextArea("", 6, 45); add(txta);
    Первоначальный цвет фона многострочного текстового поля устанавливается тем же способом, чтомы использовали для других компонент:
    txta.setBackground(Color.white);
    Этот цвет в дальнейшем будет изменяться обработчиком событий, создаваемых списком цветов.
    И, наконец, последнее что делает метод init перед тем как вернуть управление, - создает кнопку с надписью Ready и добавляет ее в окно аплета:
    btReady = new Button("Ready"); add(btReady);

    Метод main

    Итак, виртуальная машина реализуется приложением операционной системы и запускается по обычным правилам. Программа, написанная на Java, является набором классов. Понятно, что требуется некая входная точка, с которой должно начинаться выполнение приложения.
    Такой входной точкой, по аналогии с языками C/C++, является метод main(). Пример его объявления:
    public static void main(String[] args) { }
    Модификатор static в этой лекции не рассматривался и будет изучен позже. Он позволяет вызвать метод main(), не создавая объектов. Метод не возвращает никакого значения, хотя в C есть возможность указать код возврата из программы. В Java для этой цели существует метод System.exit(), который закрывает виртуальную машину и имеет аргумент типа int.
    Аргументом метода main() является массив строк. Он заполняется дополнительными параметрами, которые были указаны при вызове метода.
    package test.first;
    public class Test { public static void main(String[] args) { for (int i=0; i Для вызова программы виртуальной машине передается в качестве параметра имя класса, у которого объявлен метод main(). Поскольку это имя класса, а не имя файла, то не должно указываться никакого расширения (.class или .java) и расположение класса записывается через точку (разделитель имен пакетов), а не с помощью файлового разделителя. Компилятору же, напротив, передается имя и путь к файлу.
    Если приведенный выше модуль компиляции сохранен в файле Test.java, который лежит в каталоге test\first, то вызов компилятора записывается следующим образом:
    javac test\first\Test.java
    А вызов виртуальной машины:
    java test.first.Test
    Чтобы проиллюстрировать работу с параметрами, изменим строку запуска приложения:
    java test.first.Test Hello, World!
    Результатом работы программы будет:
    Hello, World!

    Методы

  • addNotify

  • Вызов метода createButton
    public void addNotify();
    Получение надписи на кнопке
  • getLabel

  • public String getLabel();
    Получение строки параметров, отражающей состояние кнопки
  • paramString

  • protected String paramString();
    Установка надписи на кнопке
  • setLabel

  • public void setLabel(String label);
    Из методов класса Button вы будете использовать чаще всего два - getLabel и setLabel. Первый из них позволяет получить строку надписи на кнопке, а второй - установить новую надпись.
    Обычно аплет создает в своем окне кнопки в процессе своей инициализации при обработке метода init, например:
    Button btn1; . . . public void init() { btn1 = new Button("Button 1"); add(btn1); }
    Здесь мы создали кнопку с надписью Button 1. Затем мы добавили эту кнопку в контейнер, которым является окно аплета, с помощью метода add.

  • addNotify

  • Вызов метода createCheckbox
    public void addNotify();
  • getCheckboxGroup

  • Получение группы, к которой относится данный переключатель с зависимой фиксацией
    public CheckboxGroup getCheckboxGroup();
  • getLabel

  • Получение названия переключателя
    public String getLabel();
  • getState

  • Определение текущего состояния переключателя
    public boolean getState();
  • paramString

  • Получение строки параметров
    protected String paramString();
  • setCheckboxGroup

  • Установка группы, к которой относится данный переключатель с зависимой фиксацией
    public void setCheckboxGroup(CheckboxGroup g);
  • setLabel

  • Установка названия переключателя
    public void setLabel(String label);
  • setState

  • Установка нового состояния переключателя
    public void setState(boolean state);


    Получение ссылки на переключатель, который находится во включенном состоянии
    public Checkbox getCurrent();
    Установка указанного переключателя в группе во включенное состояние
    public void setCurrent(Checkbox box);
    Получение строки, которая представляет группу
    public String toString();
    Ссылка на этот объект указывается при создании отдельных переключателей с зависимой фиксацией, входящих в группу:
    CheckboxGroup grModeGroup; Checkbox rdbox1; Checkbox rdbox2; Checkbox rdbox3; Checkbox rdbox4; . . . public void init() { grModeGroup = new CheckboxGroup();
    rdbox1 = new Checkbox("Mode 1", grModeGroup, true); rdbox2 = new Checkbox("Mode 2", grModeGroup, false); rdbox3 = new Checkbox("Mode 3", grModeGroup, false); rdbox4 = new Checkbox("Mode 4", grModeGroup, false);
    add(rdbox1); add(rdbox2); add(rdbox3); add(rdbox4); }
    Через первый параметр конструктору Checkbox в этом примере передается название переключателя, через второй - ссылка на группу, а через третий - состояние, в которое должен быть установлен переключатель. Из всех переключателей группы только один может находиться во включенном состоянии.
    Назад Вперед

    МетодыМетоды Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Методы
    printmenus();
    МетодыПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Методы»


    МетодыSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Методы»


    МетодыКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Методы»


    МетодыГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Методы»


    МетодыОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Методы»


    МетодыПроекты
    События
    Lab Downloads
    Посмотреть все Методы»


    Методы


  • addItem

  • Добавление элемента в список
    public void addItem(String item);
  • addNotify

  • Вызов метода createChoice
    public void addNotify();
  • countItems

  • Определение количества элементов в списке
    public int countItems();
  • getItem

  • Получение строки списка по номеру соответствующего ему элемента списка
    public String getItem(int index);
  • getSelectedIndex

  • Получение номера текущего выбранного элемента
    public int getSelectedIndex();
  • getSelectedItem

  • Получение строки, соответствующей текущему выбранному элементу списка
    public String getSelectedItem();
  • paramString

  • Получение строки параметров
    protected String paramString();
  • select

  • Выбор в списке элемента по заданному номеру
    public void select(int pos);
  • select

  • Выбор в списке элемента по заданной строке
    public void select(String str);


  • addItem

  • Добавление элемента в список
    public void addItem(String item);
    Добавление элемента в список с указанием номера позиции
    public void addItem(String item, int index);
  • addNotify

  • Вызов метода createList
    public void addNotify();
  • allowsMultipleSelections

  • Переключение списка в режим, при котором возможно выбирать одновременно несколько элементов
    public boolean allowsMultipleSelections();
  • clear

  • Удаление из списка всех элементов
    public void clear();
  • countItems

  • Определение количества элементов в списке
    public int countItems();
  • delItem

  • Удаление элемента из заданной позиции
    public void delItem(int position);
  • delItems

  • Удаление нескольких элементов
    public void delItems(int start, int end);
  • deselect

  • Отмена выделения элемента с заданной позицией
    public void deselect(int index);
  • getItem

  • Получение строки, связанной с элементом, по позиции этого элемента
    public String getItem(int index);
  • getRows

  • Определение количества элементов, которые видны в окне списка
    public int getRows();
  • getSelectedIndex

  • Определение номера выделенного элемента
    public int getSelectedIndex();
  • getSelectedIndexes

  • Определение номеров выделенных элементов
    public int[] getSelectedIndexes();
  • getSelectedItem

  • Получение текстовой строки, связанной с выделенным элементом
    public String getSelectedItem();
  • getSelectedItems

  • Получение ссылки на массив строк, связанных с выделенными элементами
    public String[] getSelectedItems();
  • getVisibleIndex

  • Определение номера элемента массива, который был сделан в последний раз выделенным с помощью метода makeVisible
    public int getVisibleIndex();
  • isSelected

  • Проверка, является ли выделенной строка с заданным номером
    public boolean isSelected(int index);
  • makeVisible

  • Выполняется свертка элементов списка таким образом, чтобы элемент с заданным номером стал видимым
    public void makeVisible(int index);
  • minimumSize

  • Минимальные размеры области, необходимые для отображения списка
    public Dimension minimumSize();


  • addNotify

  • Вызов метода createLabel
    public void addNotify();
  • getAlignment

  • Определение текущего выравнивания текстового поля
    public int getAlignment();
  • getText

  • Получение текста из поля
    public String getText();
  • paramString

  • Получение строки параметров
    protected String paramString();
  • setAlignment

  • Установка выравнивания текстового поля
    public void setAlignment(int alignment);
  • setText

  • Запись текста в поле
    public void setText(String label);


  • addNotify

  • Вызов метода createTextField
    public void addNotify();
  • echoCharIsSet

  • Проверка, установлен ли для поля эхо-символ
    public boolean echoCharIsSet();
  • getColumns

  • Определение размера поля
    public int getColumns();
  • getEchoChar

  • Получение текущего эхо-символа
    public char getEchoChar();
  • minimumSize

  • Определение минимальных размеров области для отображения поля
    public Dimension minimumSize();
    Определение минимальных размеров области для отображения поля заданной ширины
    public Dimension minimumSize(int cols);
  • paramString

  • Получение строки параметров
    protected String paramString();
  • preferredSize

  • Определение оптимальных размеров области для отображения поля
    public Dimension preferredSize();
    Определение оптимальных размеров области для отображения поля заданной ширины
    public Dimension preferredSize(int cols);
  • setEchoCharacter

  • Установка эхо-символа для отображения в поле
    public void setEchoCharacter(char c);


  • getSelectedText

  • Получение текста, выделенного пользователем в окне поля
    public String getSelectedText();
  • getSelectionEnd

  • Получение позиции конца выделенной области
    public int getSelectionEnd();
  • getSelectionStart

  • Получение позиции начала выделенной области
    public int getSelectionStart();
  • getText

  • Получение полного текста из поля
    public String getText();
  • isEditable

  • Проверка, возможно ли редактирование текста в поле
    public boolean isEditable();
  • paramString

  • Получение строки параметров
    protected String paramString();
  • removeNotify

  • Удаление извещения
    public void removeNotify();
  • select

  • Выделение заданной области текста
    public void select(int selStart, int selEnd);
  • selectAll

  • Выделение всего текста
    public void selectAll();
  • setEditable

  • Включение или выключение возможности редактирования текста
    public void setEditable(boolean t);
  • setText

  • Установка текста в поле
    public void setText(String t);
    С помощью метода getText вы можете получить весь текст, который имеется в поле. Метод getSelectedText позволяет получить только ту часть текста, которая предварительно была выделена пользователем.
    Приложение может выделить любой фрагмент текста или весь текст при помощи методов select и selectAll, соответственно.
    Для записи текста в поле приложение может воспользоваться методом setText.
    Возможно, для вас будет интересен метод setEditable, позволяющий переключать текстовое поля из режима, при котором редактирование заблокировано, в режим с разрешенным редактированием и обратно.
    Назад Вперед

    МетодыМетоды Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Методы
    printmenus();
    МетодыПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Методы»


    МетодыSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Методы»


    МетодыКаталог решений


  • addNotify

  • Вызов метода createTextArea
    public void addNotify();
  • append

  • Добавление текста в поле редактирования
    public void append(String str);
  • appendText

  • Добавление текста в поле редактирования. Этот метод устарел. Вы должны использовать вместо него метод append, описанный выше.
    public void appendText(String str);
  • getColumns

  • Определение количества столбцов поля
    public int getColumns();
  • getRows

  • Определение количества строк поля
    public int getRows();
  • insertText

  • Добавление текста в поле редактирования начиная с заданной позиции
    public void insertText(String str, int pos);
  • minimumSize

  • Определение минимальных размеров области для размещения многострочного текстового поля
    public Dimension minimumSize();
    Определение минимальных размеров области для размещения многострочного текстового поля с заданным количеством строк и столбцов
    public Dimension minimumSize(int rows, int cols);
  • paramString

  • Получение строки параметров
    protected String paramString();
  • preferredSize

  • Определение предпочтительных размеров области для размещения многострочного текстового поля
    public Dimension preferredSize();
    Определение предпочтительных размеров области для размещения многострочного текстового поля с заданным количеством строк и столбцов
    public Dimension preferredSize(int rows, int cols);
  • replaceText

  • Замещение блока текста, начиная с первой позиции и до второй позиции
    public void replaceText(String str, int start, int end);

    Многострочное текстовое поле класса TextArea

    Назад Вперед
    Если вам нужно поле для ввода многострочной информации, обратите внимание на класс TextArea. С его помощью вы можете создать многострочное поле заданной ширины и высоты, снабженное полосами просмотра.
    Класс TextArea создан на базе класса TextComponent, рассмотренном нами ранее, поэтому для работы с многострочными полями вы можете использовать методы этого класса. В частности, вам доступен метод, с помощью которого можно получать из онка редактирования не весь текст, а только выделенную пользователем область.

    Модификаторы доступа

    Во многих языках существуют права доступа, которые ограничивают возможность использования, например, переменной в классе. Например, легко представить два крайних вида прав доступа: это public, когда поле доступно из любой точки программы, и private, когда поле может использоваться только внутри того класса, в котором оно объявлено.
    Однако прежде, чем переходить к подробному рассмотрению этих и других модификаторов доступа, необходимо внимательно разобраться, зачем они вообще нужны.

    Объявление классов

    Рассмотрим базовые возможности объявления классов.
    Объявление класса состоит из заголовка и тела класса.

    Объявление конструкторов

    Формат объявления конструкторов похож на упрощенное объявление методов. Также выделяют заголовок и тело конструктора. Заголовок состоит, во-первых, из модификаторов доступа (никакие другие модификаторы недопустимы). Во-вторых, указывается имя класса, которое можно расценивать двояко. Можно считать, что имя конструктора совпадает с именем класса. А можно рассматривать конструктор как безымянный, а имя класса – как тип возвращаемого значения, ведь конструктор может породить только объект класса, в котором он объявлен. Это исключительно дело вкуса, так как на формате объявления никак не сказывается:
    public class Human { private int age;
    protected Human(int a) { age=a; }
    public Human(String name, Human mother, Human father) { age=0; } }
    Как видно из примеров, далее следует перечисление входных аргументов по тем же правилам, что и для методов. Завершает заголовок конструктора throws-выражение (в примере не использовано, см. лекцию 10 "Исключения"). Оно имеет особую важность для конструкторов, поскольку сгенерировать ошибку – это для конструктора единственный способ не создавать объект. Если конструктор выполнился без ошибок, то объект гарантированно создается.
    Тело конструктора пустым быть не может и поэтому всегда описывается в фигурных скобках (для простейших реализаций скобки могут быть пустыми).
    В отсутствие имени (или из-за того, что у всех конструкторов одинаковое имя, совпадающее с именем класса) сигнатура конструктора определяется только набором входных параметров по тем же правилам, что и для методов. Аналогично, в одном классе допускается любое количество конструкторов, если у них различные сигнатуры.
    Тело конструктора может содержать любое количество return-выражений без аргументов. Если процесс исполнения дойдет до такого выражения, то на этом месте выполнение конструктора будет завершено.
    Однако логика работы конструкторов имеет и некоторые важные особенности. Поскольку при их вызове осуществляется создание и инициализация объекта, становится понятно, что такой процесс не может происходить без обращения к конструкторам всех родительских классов. Поэтому вводится обязательное правило – первой строкой в конструкторе должно быть обращение к родительскому классу, которое записывается с помощью ключевого слова super.
    public class Parent { private int x, y;

    public Parent() { x=y=0; }

    public Parent(int newx, int newy) { x=newx; y=newy; } }

    public class Child extends Parent { public Child() { super(); }

    public Child(int newx, int newy) { super(newx, newy); } }

    Как видно, обращение к родительскому конструктору записывается с помощью super, за которым идет перечисление аргументов. Этот набор определяет, какой из родительских конструкторов будет использован. В приведенном примере в каждом классе имеется по два конструктора и каждый конструктор в наследнике обращается к аналогичному в родителе (это довольно распространенный, но, конечно, не обязательный способ).

    Проследим мысленно весь алгоритм создания объекта. Он начинается при исполнении выражения с ключевым словом new, за которым следует имя класса, от которого будет порождаться объект, и набор аргументов для его конструктора. По этому набору определяется, какой именно конструктор будет использован, и происходит его вызов. Первая строка его тела содержит вызов родительского конструктора. В свою очередь, первая строка тела конструктора родителя будет содержать вызов к его родителю, и так далее. Восхождение по дереву наследования заканчивается, очевидно, на классе Object, у которого есть единственный конструктор без параметров. Его тело пустое (записывается парой пустых фигурных скобок), однако можно считать, что именно в этот момент JVM порождает объект и далее начинается процесс его инициализации. Выполнение начинает обратный путь вниз по дереву наследования. У самого верхнего родителя, прямого наследника от Object, происходит продолжение исполнения конструктора со второй строки. Когда он будет полностью выполнен, необходимо перейти к следующему родителю, на один уровень наследования вниз, и завершить выполнение его конструктора, и так далее. Наконец, можно будет вернуться к конструктору исходного класса, который был вызван с помощью new, и также продолжить его выполнение со второй строки. По его завершении объект считается полностью созданным, исполнение выражения new будет закончено, а в качестве результата будет возвращена ссылка на порожденный объект.


    Проиллюстрируем этот алгоритм следующим примером:

    public class GraphicElement { private int x, y; // положение на экране

    public GraphicElement(int nx, int ny) { super(); // обращение к конструктору // родителя Object System.out.println("GraphicElement"); x=nx; y=ny; } }

    public class Square extends GraphicElement { private int side;

    public Square(int x, int y, int nside) { super(x, y); System.out.println("Square"); side=nside; } }

    public class SmallColorSquare extends Square { private Color color;

    public SmallColorSquare(int x, int y, Color c) { super(x, y, 5); System.out.println("SmallColorSquare"); color=c; } }

    После выполнения выражения создания объекта на экране появится следующее:

    GraphicElement Square SmallColorSquare

    Выражение super может стоять только на первой строке конструктора. Часто можно увидеть конструкторы вообще без такого выражения. В этом случае компилятор первой строкой по умолчанию добавляет вызов родительского конструктора без параметров (super()). Если у родительского класса такого конструктора нет, выражение super обязательно должно быть записано явно (и именно на первой строке), поскольку необходима передача входных параметров.

    Напомним, что, во-первых, конструкторы не имеют имени и их нельзя вызвать явно, только через выражение создания объекта. Кроме того, конструкторы не передаются по наследству. То есть, если в родительском классе объявлено пять разных полезных конструкторов и требуется, чтобы класс-наследник имел аналогичный набор, необходимо все их описать заново.

    Класс обязательно должен иметь конструктор, иначе невозможно порождать объекты ни от него, ни от его наследников. Поэтому если в классе не объявлен ни один конструктор, компилятор добавляет один по умолчанию. Это public-конструктор без параметров и с телом, описанным парой пустых фигурных скобок. Из этого следует, что такое возможно только для классов, у родителей которых объявлен конструктор без параметров, иначе возникнет ошибка компиляции. Обратите внимание, что если затем в такой класс добавляется конструктор (не важно, с параметрами или без), то конструктор по умолчанию больше не вставляется:


    /* * Этот класс имеет один конструктор. */ public class One { // Будет создан конструктор по умолчанию // Родительский класс Object имеет // конструктор без параметров. }

    /* * Этот класс имеет один конструктор. */ public class Two { // Единственный конструктор класса Two. // Выражение new Two() ошибочно! public Two(int x) { } }

    /* * Этот класс имеет два конструктора. */ public class Three extends Two { public Three() { super(1); // выражение super требуется }

    public Three(int x) { super(x); // выражение super требуется } }

    Если класс имеет более одного конструктора, допускается в первой строке некоторых из них указывать не super, а this – выражение, вызывающее другой конструктор этого же класса.

    Рассмотрим следующий пример:

    public class Vector { private int vx, vy; protected double length;

    public Vector(int x, int y) { super(); vx=x; vy=y; length=Math.sqrt(vx*vx+vy*vy); }

    public Vector(int x1, int y1, int x2, int y2) { super(); vx=x2-x1; vy=y2-y1; length=Math.sqrt(vx*vx+vy*vy); } }

    Видно, что оба конструктора совершают практически идентичные действия, поэтому можно применить более компактный вид записи:

    public class Vector { private int vx, vy; protected double length;

    public Vector(int x, int y) { super(); vx=x; vy=y; length=Math.sqrt(vx*vx+vy*vy); }

    public Vector(int x1, int y1, int x2, int y2) { this(x2-x1, y2-y1); } }

    Большим достоинством такого метода записи является то, что удалось избежать дублирования идентичного кода. Например, если процесс инициализации объектов этого класса удлинится на один шаг (скажем, добавится проверка длины на ноль), то такое изменение надо будет внести только в первый конструктор. Такой подход помогает избежать случайных ошибок, так как исчезает необходимость тиражировать изменения в нескольких местах.

    Разумеется, такое обращение к конструкторам своего класса не должно приводить к зацикливаниям, иначе будет выдана ошибка компиляции. Цепочка this должна в итоге приводить к super, который должен присутствовать (явно или неявно) хотя бы в одном из конструкторов. После того, как отработают конструкторы всех родительских классов, будет продолжено выполнение каждого конструктора, вовлеченного в процесс создания объекта.


    public class Test { public Test() { System.out.println("Test()"); }

    public Test(int x) { this(); System.out.println("Test(int x)"); } }

    После выполнения выражения new Test(0) на консоли появится:

    Test() Test(int x)

    В заключение рассмотрим применение модификаторов доступа для конструкторов. Может вызвать удивление возможность объявлять конструкторы как private. Ведь они нужны для генерации объектов, а к таким конструкторам ни у кого не будет доступа. Однако в ряде случаев модификатор private может быть полезен. Например:



  • private-конструктор может содержать инициализирующие действия, а остальные конструкторы будут использовать его с помощью this, причем прямое обращение к этому конструктору по каким-то причинам нежелательно;
  • запрет на создание объектов этого класса, например, невозможно создать экземпляр класса Math;
  • реализация специального шаблона проектирования из ООП Singleton, для работы которого требуется контролировать создание объектов, что невозможно в случае наличия не-private конструкторов.


  • Объявление методов

    Объявление метода состоит из заголовка и тела метода. Заголовок состоит из:
  • модификаторов (доступа в том числе);
  • типа возвращаемого значения или ключевого слова void;
  • имени метода;
  • списка аргументов в круглых скобках (аргументов может не быть);
  • специального throws-выражения.

  • Заголовок начинается с перечисления модификаторов. Для методов доступен любой из трех возможных модификаторов доступа. Также допускается использование доступа по умолчанию.
    Кроме того, существует модификатор final, который говорит о том, что такой метод нельзя переопределять в наследниках. Можно считать, что все методы final-класса, а также все private-методы любого класса, являются final.
    Также поддерживается модификатор native. Метод, объявленный с таким модификатором, не имеет реализации на Java. Он должен быть написан на другом языке (C/C++, Fortran и т.д.) и добавлен в систему в виде загружаемой динамической библиотеки (например, DLL для Windows). Существует специальная спецификация JNI (Java Native Interface), описывающая правила создания и использования native-методов.
    Такая возможность для Java необходима, поскольку многие компании имеют обширные программные библиотеки, написанные на более старых языках. Их было бы очень трудоемко и неэффективно переписывать на Java, поэтому необходима возможность подключать их в таком виде, в каком они есть. Безусловно, при этом Java-приложения теряют целый ряд своих преимуществ, таких, как переносимость, безопасность и другие. Поэтому применять JNI следует только в случае крайней необходимости.
    Эта спецификация накладывает требования на имена процедур во внешних библиотеках (она составляет их из имени пакета, класса и самого native-метода), а поскольку библиотеки менять, как правило, очень неудобно, часто пишут специальные библиотеки-"обертки", к которым обращаются Java-классы через JNI, а они сами обращаются к целевым модулям.
    Наконец, существует еще один специальный модификатор synchronized, который будет рассмотрен в лекции, описывающей потоки выполнения.

    После перечисления модификаторов указывается имя (простое или составное) типа возвращаемого значения; это может быть как примитивный, так и объектный тип. Если метод не возвращает никакого значения, указывается ключевое слово void.

    Затем определяется имя метода. Указанный идентификатор при объявлении становится простым именем метода. Составное имя формируется из имени класса или имени переменной объектного типа и простого имени метода. Областью видимости метода является все объявление тела класса.

    Аргументы метода перечисляются через запятую. Для каждого указывается сначала тип, затем имя параметра. В отличие от объявления переменной здесь запрещается указывать два имени для одного типа:

    // void calc (double x, y); - ошибка! void calc (double x, double y);

    Если аргументы отсутствуют, указываются пустые круглые скобки. Одноименные параметры запрещены. Создание локальных переменных в методе, с именами, совпадающими с именами параметров, запрещено. Для каждого аргумента можно ввести ключевое слово final перед указанием его типа. В этом случае такой параметр не может менять своего значения в теле метода (то есть участвовать в операции присвоения в качестве левого операнда).

    public void process(int x, final double y) { x=x*x+Math.sqrt(x);

    // y=Math.sin(x); - так писать нельзя, // т.к. y - final! }

    О том, как происходит изменение значений аргументов метода, рассказано в конце этой лекции.

    Важным понятием является сигнатура (signature) метода. Сигнатура определяется именем метода и его аргументами (количеством, типом, порядком следования). Если для полей запрещается совпадение имен, то для методов в классе запрещено создание двух методов с одинаковыми сигнатурами.

    Например,

    class Point { void get() {} void get(int x) {} void get(int x, double y) {} void get(double x, int y) {} }

    Такой класс объявлен корректно. Следующие пары методов в одном классе друг с другом несовместимы:

    void get() {} int get() {}

    void get(int x) {} void get(int y) {}

    public int get() {} private int get() {}


    В первом случае методы отличаются типом возвращаемого значения, которое, однако, не входит в определение сигнатуры. Стало быть, это два метода с одинаковыми сигнатурами и они не могут одновременно появиться в объявлении тела класса. Можно составить пример, который создал бы неразрешимую проблему для компилятора, если бы был допустим:

    // пример вызовет ошибку компиляции class Test { int get() { return 5; } Point get() { return new Point(3,5); }

    void print(int x) { System.out.println("it's int! "+x); } void print(Point p) { System.out.println("it's Point! "+p.x+ ", "+p.y); }

    public static void main (String s[]) { Test t = new Test(); t.print(t.get()); // Двусмысленность! } }

    В классе определена запрещенная пара методов get() с одинаковыми сигнатурами и различными возвращаемыми значениями. Обратимся к выделенной строке в методе main, где возникает конфликтная ситуация, с которой компилятор не может справиться. Определены два метода print() (у них разные аргументы, а значит, и сигнатуры, то есть это допустимые методы), и чтобы разобраться, какой из них будет вызван, нужно знать точный тип возвращаемого значения метода get(), что невозможно.

    На основе этого примера можно понять, как составлено понятие сигнатуры. Действительно, при вызове указывается имя метода и перечисляются его аргументы, причем компилятор всегда может определить их тип. Как раз эти понятия и составляют сигнатуру, и требование ее уникальности позволяет компилятору всегда однозначно определить, какой метод будет вызван.

    Точно так же в предыдущем примере вторая пара методов различается именем аргументов, которые также не входят в определение сигнатуры и не позволяют определить, какой из двух методов должен быть вызван.

    Аналогично, третья пара различается лишь модификаторами доступа, что также недопустимо.

    Наконец, завершает заголовок метода throws-выражение. Оно применяется для корректной работы с ошибками в Java и будет подробно рассмотрено в соответствующей лекции.


    Пример объявления метода:

    public final java.awt.Point createPositivePoint( int x, int y) throws IllegalArgumentException { return (x>0 && y>0) ? new Point(x, y) : null; }

    Далее, после заголовка метода следует тело метода. Оно может быть пустым и тогда записывается одним символом "точка с запятой". Native-методы всегда имеют только пустое тело, поскольку настоящая реализация написана на другом языке.

    Обычные же методы имеют непустое тело, которое описывается в фигурных скобках, что показано в многочисленных примерах в этой и других лекциях. Если текущая реализация метода не выполняет никаких действий, тело все равно должно описываться парой пустых фигурных скобок:

    public void empty() {}

    Если в заголовке метода указан тип возвращаемого значения, а не void, то в теле метода обязательно должно встречаться return-выражение. При этом компилятор проводит анализ структуры метода, чтобы гарантировать, что при любых операторах ветвления возвращаемое значение будет сгенерировано. Например, следующий пример является некорректным:

    // пример вызовет ошибку компиляции public int get() { if (condition) { return 5; } }

    Видно, что хотя тело метода содержит return-выражение, однако не при любом развитии событий возвращаемое значение будет сгенерировано. А вот такой пример является верным:

    public int get() { if (condition) { return 5; } else { return 3; } }

    Конечно, значение, указанное после слова return, должно быть совместимо по типу с объявленным возвращаемым значением (это понятие подробно рассматривается в лекции 7).

    В методе без возвращаемого значения (указано void) также можно использовать выражение return без каких-либо аргументов. Его можно указать в любом месте метода и в этой точке выполнение метода будет завершено:

    public void calculate(int x, int y) { if (x<=0 || y<=0) { return; // некорректные входные // значения, выход из метода } ... // основные вычисления }

    Выражений return (с параметром или без для методов с/без возвращаемого значения) в теле одного метода может быть сколько угодно. Однако следует помнить, что множество точек выхода в одном методе может заметно усложнить понимание логики его работы.

    Объявление полей

    Объявление полей начинается с перечисления модификаторов. Возможно применение любого из трех модификаторов доступа, либо никакого вовсе, что означает уровень доступа по умолчанию.
    Поле может быть объявлено как final, это означает, что оно инициализируется один раз и больше не будет менять своего значения. Простейший способ работы с final-переменными - инициализация при объявлении:
    final double PI=3.1415;
    Также допускается инициализация final-полей в конце каждого конструктора класса.
    Не обязательно использовать для инициализации константы компиляции, возможно обращение к различным функциям, например:
    final long creationTime = System.currentTimeMillis();
    Данное поле будет хранить время создания объекта. Существует еще два специальных модификатора - transient и volatile. Они будут рассмотрены в соответствующих лекциях.
    После списка модификаторов указывается тип поля. Затем идет перечисление одного или нескольких имен полей с возможными инициализаторами:
    int a; int b=3, c=b+5, d; Point p, p1=null, p2=new Point();
    Повторяющиеся имена полей запрещены. Указанный идентификатор при объявлении становится простым именем поля. Составное имя формируется из имени класса или имени переменной объектного типа, и простого имени поля. Областью видимости поля является все объявление тела класса.
    Запрещается использовать поле в инициализации других полей до его объявления.
    int y=x; int x=3;
    Однако в остальном поля можно объявлять и ниже их использования:
    class Point { int getX() {return x;}
    int y=getX(); int x=3;
    public static void main (String s[]) { Point p=new Point(); System.out.println(p.x+", "+p.y); } }
    Результатом будет:
    3, 0
    Данный пример корректен, но для понимания его результата необходимо вспомнить, что все поля класса имеют значение по умолчанию:
  • для числовых полей примитивных типов – 0;
  • для булевского типа – false;
  • для ссылочных – null.

  • Таким образом, при инициализации переменной y был использован результат метода getX(), который вернул значение по умолчанию переменной x, то есть 0. Затем переменная x получила значение 3.

    Обработка событий от кнопки

    Для обработки событий, создаваемых кнопками и другими компонентами, вы можете переопределить метод handleEvent. Однако существует и более простой способ.
    Этот способ основан на переопределении метода action, который получает управление, когда пользователь совершает какое-либо действие с компонентом. Под действием подразумевается нажатие на кнопку, завершение ввода текстовой строки, выбор элемента из списка, изменение состояния переключателя и так далее.
    Прототип метода action представлен ниже:
    public boolean action(Event evt, Object obj) { . . . }
    В качестве первого параметра методу передается ссылка на объект класса Event, содержащий всю информацию о событии. Второй параметр представляет собой ссылку на объект, вызвавший появление события.
    Как обрабатывать событие в методе action?
    Прежде всего необходимо проверить, объект какого типа создал событие. Это можно сделать, например, следующим образом:
    if(evt.target instanceof Button) { . . . return true; } return false;
    Здесь мы с помощью оператора instanceof проверяем, является ли объект, вызвавший появление события, объектом класса Button.
    Далее, если в окне аплета имеется несколько кнопок, необходимо выполнить ветвление по ссылкам на объекты кнопок, как это показано ниже:
    if(evt.target.equals(btn1)) { . . . } else if(evt.target.equals(btn2)) { . . . }
    . . .
    else { return false; } return true;
    Тем из вас, кто создавал приложения Windows на языке программирования С, этот фрагмент кода может напомнить длинный переключатель switch обработки сообщений Windows.
    Назад Вперед

    Обработка событий от кнопкиОбработка событий от кнопки Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Обработка событий от кнопки
    printmenus();
    Обработка событий от кнопкиПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Обработка событий от кнопки»


    Обработка событий от кнопкиSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Обработка событий от кнопки»


    Обработка событий от кнопкиКаталог решений

    Обработка событий от списка класса List

    В отличие от списка класса Choice, для выбора строки (или нескольких строк) из списка класса List, пользователь должен сделать двойной щелчок левой клавишей мыши по выделенному элементу (или элементам, если выделено несколько элементов). При этом событие можно обработать переопределенным методом action, как мы это делали для списка класса Choice.
    Однако список класса List создает события не только при двойном щелчке, но и при выделении или отмены выделения элементов, сделанном пользователем одинарным щелчком клавиши мыши. Аплет может перехватывать и обрабатывать такие события, переопределив метод handleEvent.
    Назад Вперед

    Обработка событий от списка класса ListОбработка событий от списка класса List Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Обработка событий от списка класса List
    printmenus();
    Обработка событий от списка класса ListПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Обработка событий от списка класса List»


    Обработка событий от списка класса ListSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Обработка событий от списка класса List»


    Обработка событий от списка класса ListКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Обработка событий от списка класса List»


    Обработка событий от списка класса ListГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Обработка событий от списка класса List»


    Обработка событий от списка класса ListОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Обработка событий от списка класса List»


    Обработка событий от списка класса ListПроекты
    События
    Lab Downloads
    Посмотреть все Обработка событий от списка класса List»


    Обработка событий от списка класса List

    Описание класса List

    В классе List определено два конструктора и довольно много различных методов. Ниже мы привели краткое описание класса List:

    Параметры методов

    Для лучшего понимания работы с параметрами методов в Java необходимо рассмотреть несколько вопросов.
    Как передаются аргументы в методы – по значению или по ссылке? С точки зрения программы вопрос формулируется, например, следующим образом. Пусть есть переменная и она в качестве аргумента передается в некоторый метод. Могут ли произойти какие-либо изменения с этой переменной после завершения работы метода?
    int x=3; process(x); print(x);
    Предположим, используемый метод объявлен следующим образом:
    public void process(int x) { x=5; }
    Какое значение появится на консоли после выполнения примера? Чтобы ответить на этот вопрос, необходимо вспомнить, как переменные разных типов хранят свои значения в Java.
    Напомним, что примитивные переменные являются истинными хранилищами своих значений и изменение значения одной переменной никогда не скажется на значении другой. Параметр метода process(), хоть и имеет такое же имя x, на самом деле является полноценным хранилищем целочисленной величины. А потому присвоение ему значения 5 не скажется на внешних переменных. То есть результатом примера будет 3 и аргументы примитивного типа передаются в методы по значению. Единственный способ изменить такую переменную в результате работы метода – возвращать нужные величины из метода и использовать их при присвоении:
    public int double(int x) { return x+x; }
    public void test() { int x=3; x=double(x); }
    Перейдем к ссылочным типам.
    public void process(Point p) { p.x=3; }
    public void test() { Point p = new Point(1,2); process(p); print(p.x); }
    Ссылочная переменная хранит ссылку на объект, находящийся в памяти виртуальной машины. Поэтому аргумент метода process() будет иметь в качестве значения ту же самую ссылку и, стало быть, ссылаться на тот же самый объект. Изменения состояния объекта, осуществленные с помощью одной ссылки, всегда видны при обращении к этому объекту с помощью другой. Поэтому результатом примера будет значение 3. Объектные значения передаются в Java по ссылке. Однако если изменять не состояние объекта, а саму ссылку, то результат будет другим:
    public void process(Point p) { p = new Point(4,5); }
    public void test() { Point p = new Point(1,2); process(p); print(p.x); }
    В этом примере аргумент метода process() после присвоения начинает ссылаться на другой объект, нежели исходная переменная p, а значит, результатом примера станет значение 1. Можно сказать, что ссылочные величины передаются по значению, но значением является именно ссылка на объект.
    Теперь можно уточнить, что означает возможность объявлять параметры методов и конструкторов как final. Поскольку изменения значений параметров (но не объектов, на которые они ссылаются) никак не сказываются на переменных вне метода, модификатор final говорит лишь о том, что значение этого параметра не будет меняться на протяжении работы метода. Разумеется, для аргумента final Point p выражение p.x=5 является допустимым (запрещается p=new Point(5, 5)).

    Перегруженные методы

    Перегруженными (overloaded) методами называются методы одного класса с одинаковыми именами. Сигнатуры у них должны быть различными и различие может быть только в наборе аргументов.
    Если в классе параметры перегруженных методов заметно различаются: например, у одного метода один параметр, у другого – два, то для Java это совершенно независимые методы и совпадение их имен может служить только для повышения наглядности работы класса. Каждый вызов, в зависимости от количества параметров, однозначно адресуется тому или иному методу.
    Однако если количество параметров одинаковое, а типы их различаются незначительно, при вызове может сложиться двойственная ситуация, когда несколько перегруженных методов одинаково хорошо подходят для использования. Например, если объявлены типы Parent и Child, где Child расширяет Parent, то для следующих двух методов:
    void process(Parent p, Child c) {} void process(Child c, Parent p) {}
    можно сказать, что они допустимы, их сигнатуры различаются. Однако при вызове
    process(new Child(), new Child());
    обнаруживается, что оба метода одинаково годятся для использования. Другой пример, методы:
    process(Object o) {} process(String s) {}
    и примеры вызовов:
    process(new Object()); process(new Point(4,5)); process("abc");
    Очевидно, что для первых двух вызовов подходит только первый метод, и именно он будет вызван. Для последнего же вызова подходят оба перегруженных метода, однако класс String является более "специфичным", или узким, чем класс Object. Действительно, значения типа String можно передавать в качестве аргументов типа Object, обратное же неверно. Компилятор попытается отыскать наиболее специфичный метод, подходящий для указанных параметров, и вызовет именно его. Поэтому при третьем вызове будет использован второй метод.
    Однако для предыдущего примера такой подход не дает однозначного ответа. Оба метода одинаково специфичны для указанного вызова, поэтому возникнет ошибка компиляции. Необходимо, используя явное приведение, указать компилятору, какой метод следует применить:
    process((Parent)(new Child()), new Child()); // или process(new Child(),(Parent)(new Child()));
    Это верно и в случае использования значения null:
    process((Parent)null, null); // или process(null,(Parent)null);

    Переключатели

    Назад Вперед
    Аплеты Java могут создавать в своем окне переключатели двух типов: с независимой фиксацией и с зависимой фиксацией.
    Переключатели с независимой фиксацией имеют прямоугольную форму и, исходя из названия, работают независимо друг от друга. Если такой переключатель находится во включенном состоянии, внутри изображения маленького квадрата появляется галочка, если в выключенном - галочка исчезает.
    Обычно переключатели с независимой фиксацией используются для управления независящими друг от друга режимами или параметрами.
    Переключатели с зависимой фиксацией имеют круглую форму. В каждый момент времени может быть включен только один такой переключатель из группы переключателей с фиксацией. Аплет может создавать несколько групп переключателей с зависимой фиксацией.
    Переключатели с зависимой фиксацией используются для выбора из нескольких взаимоисключающих возможностей, например, для установки одного из нескольких режимов.

    Поля главного класса

    В главном классе нашего аплета мы определили несколько полей.
    Поле btReady хранит ссылку на кнопку с надписью Ready:
    Button btReady;
    В полях chbox1 и chbox2 записаны ссылки на переключатели с независимой фиксацией, которые используются для активизации однострочных текстовых полей:
    Checkbox chbox1; Checkbox chbox2;
    Поле grRadio хранит ссылку на группу переключателей с зависимой фиксацией, определяющих режимы работы Mode 1, Mode 2 и Mode 3:
    CheckboxGroup grRadio;
    Ссылки на эти переключатели находятся в следующих трех полях:
    Checkbox rd1; Checkbox rd2; Checkbox rd3;
    В поле ch1 хранится ссылка на список, предназначенный для выбора цвета:
    Choice ch1;
    Слева от однострочных полей редактирования в нашем окне имеются подписи, реализованные как объекты класса Label. Ссылки на эти объекты находятся в полях lbFirstName и lbSecondName:
    Label lbFirstName; Label lbSecondName;
    Ссылки на однострочные поля редактирования записаны в поля с именами txtFirstName и txtSecondName:
    TextField txtFirstName; TextField txtSecondName;
    И, наконец, ссылка на многострочное текстовое поле хранится в поле с именем txta:
    TextArea txta;

    Поля

    Поля класса Label задают способ выравнивания текстового поля
  • CENTER

  • Центрирование
    public final static int CENTER;
  • LEFT

  • Выравнивание по левой границе
    public final static int LEFT;
  • RIGHT

  • Выравнивание по правой границе
    public final static int RIGHT;

    Предназначение модификаторов доступа

    Очень часто права доступа расцениваются как некий элемент безопасности кода: мол, необходимо защищать классы от "неправильного" использования. Например, если в классе Human (человек) есть поле age (возраст человека), то какой-нибудь программист намеренно или по незнанию может присвоить этому полю отрицательное значение, после чего объект станет работать неправильно, могут появиться ошибки. Для защиты такого поля age необходимо объявить его private.
    Это довольно распространенная точка зрения, однако нужно признать, что она далека от истины. Основным смыслом разграничения прав доступа является обеспечение неотъемлемого свойства объектной модели – инкапсуляции, то есть сокрытия реализации. Исправим пример таким образом, чтобы он корректно отражал предназначение модификаторов доступа. Итак, пусть в классе Human есть поле age целочисленного типа, и чтобы все желающие могли пользоваться этим полем, оно объявляется public.
    public class Human { public int age; }
    Проходит время, и если в группу программистов, работающих над системой, входят десятки разработчиков, логично предположить, что все, или многие, из них начнут использовать это поле.
    Может получиться так, что целочисленного типа данных будет уже недостаточно и захочется сменить тип поля на дробный. Однако если просто изменить int на double, вскоре все разработчики, которые пользовались классом Human и его полем age, обнаружат, что в их коде появились ошибки, потому что поле вдруг стало дробным, и в строках, подобных этим:
    human h = getHuman(); // получаем ссылку int i=h.age; // ошибка!!
    будет возникать ошибка из-за попытки провести неявным образом сужение примитивного типа.
    Получается, что подобное изменение (в общем, небольшое и локальное) потребует модификации многих и многих классов. Поэтому внесение его окажется недопустимым, неоправданным с точки зрения количества усилий, которые необходимо затратить. То есть, объявив один раз поле или метод как public, можно оказаться в ситуации, когда малейшие изменения (имени, типа, характеристик, правил использования) в дальнейшем станут невозможны.

    Напротив, если бы поле было объявлено как private, а для чтения и изменения его значения были бы введены дополнительные методы, ситуация поменялась бы в корне:

    public class Human { private int age; // метод, возвращающий значение age public int getAge() { return age; } // метод, устанавливающий значение age public void setAge(int a) { age=a; } }

    В этом случае с данным классом могло бы работать множество программистов и могло быть создано большое количество классов, использующих тип Human, но модификатор private дает гарантию, что никто напрямую этим полем не пользуется и изменение его типа было бы совсем несложной операцией, связанной с изменением только в одном классе.

    Получение величины возраста выглядело бы следующим образом:

    human h = getHuman(); int i=h.getAge(); // обращение через метод

    Рассмотрим, как выглядит процесс смены типа поля age:

    public class Human {

    // поле получает новый тип double private /*int*/ double age;

    // старые методы работают с округлением // значения

    public int getAge() { return (int)Math.round(age); } public void setAge(int a) { age=a; } // добавляются новые методы для работы // с типом double

    public double getExactAge() { return age; } public void setExactAge(double a) { age=a; } }

    Видно, что старые методы, которые, возможно, уже применяются во многих местах, остались без изменения. Точнее, остался без изменений их внешний формат, а внутренняя реализация усложнилась. Но такая перемена не потребует никаких модификаций остальных классов системы. Пример использования

    human h = getHuman(); int i=h.getAge(); // корректно

    остается верным, переменная i получает корректное целое значение. Однако изменения вводились для того, чтобы можно было работать с дробными величинами. Для этого были добавлены новые методы и во всех местах, где требуется точное значение возраста, необходимо обращаться к ним:

    human h = getHuman(); double d=h.getExactAge(); // точное значение возраста

    Итак, в класс была добавлена новая возможность, не потребовавшая никаких изменений кода.


    За счет чего была достигнута такая гибкость? Необходимо выделить свойства объекта, которые потребуются будущим пользователям этого класса, и сделать их доступными (в данном случае, public). Те же элементы класса, что содержат детали внутренней реализации логики класса, желательно скрывать, чтобы не образовались нежелательные зависимости, которые могут сдерживать развитие системы.

    Этот пример одновременно иллюстрирует и другое теоретическое правило написания объектов, а именно: в большинстве случаев доступ к полям лучше реализовывать через специальные методы (accessors) для чтения (getters) и записи (setters). То есть само поле рассматривается как деталь внутренней реализации. Действительно, если рассматривать внешний интерфейс объекта как целиком состоящий из допустимых действий, то доступными элементами должны быть только методы, реализующие эти действия. Один из случаев, в котором такой подход приносит необходимую гибкость, уже рассмотрен.

    Есть и другие соображения. Например, вернемся к вопросу о корректном использовании объекта и установке верных значений полей. Как следствие, правильное разграничение доступа позволяет ввести механизмы проверки входных значений:

    public void setAge(int a) { if (a>=0) { age=a; } }

    В этом примере поле age никогда не примет некорректное отрицательное значение. (Недостатком приведенного примера является то, что в случае неправильных входных данных они просто игнорируются, нет никаких сообщений, позволяющих узнать, что изменения поля возраста на самом деле не произошло; для полноценной реализации метода необходимо освоить работу с ошибками в Java.)

    Бывают и более существенные изменения логики класса. Например, данные можно начать хранить не в полях класса, а в более надежном хранилище, например, файловой системе или базе данных. В этом случае методы-аксессоры опять изменят свою реализацию и начнут обращаться к persistent storage (постоянное хранилище, например, БД) для чтения/записи значений. Если доступа к полям класса не было, а открытыми были только методы для работы с их значениями, то можно изменить код этих методов, а наружные типы, которые использовали данный класс, совершенно не изменятся, логика их работы останется той же.


    Подведем итоги. Функциональность класса необходимо разделять на открытый интерфейс, описывающий действия, которые будут использовать внешние типы, и на внутреннюю реализацию, которая применяется только внутри самого класса. Внешний интерфейс в дальнейшем модифицировать невозможно, или очень сложно, для больших систем, поэтому его требуется продумывать особенно тщательно. Детали внутренней реализации могут быть изменены на любом этапе, если они не меняют логику работы всего класса. Благодаря такому подходу реализуется одна из базовых характеристик объектной модели — инкапсуляция, и обеспечивается важное преимущество технологии ООП — модульность.

    Таким образом, модификаторы доступа вводятся не для защиты типа от внешнего пользователя, а, напротив, для защиты, или избавления, пользователя от излишних зависимостей от деталей внутренней реализации. Что же касается неправильного применения класса, то его создателям нужно стремиться к тому, чтобы класс был прост в применении, тогда таких проблем не возникнет, ведь программист не станет намеренно писать код, который порождает ошибки в его программе.

    Конечно, такое разбиение на внешний интерфейс и внутреннюю реализацию не всегда очевидно, часто условно. Для облегчения задачи технических дизайнеров классов в Java введено не два (public и private), а четыре уровня доступа. Рассмотрим их и весь механизм разграничения доступа в Java более подробно.

    Разграничение доступа в Java

    Уровень доступа элемента языка является статическим свойством, задается на уровне кода и всегда проверяется во время компиляции. Попытка обратиться к закрытому элементу вызовет ошибку.
    В Java модификаторы доступа указываются для:
  • типов (классов и интерфейсов) объявления верхнего уровня;
  • элементов ссылочных типов (полей, методов, внутренних типов);
  • конструкторов классов.

  • Как следствие, массив также может быть недоступен в том случае, если недоступен тип, на основе которого он объявлен.
    Все четыре уровня доступа имеют только элементы типов и конструкторы. Это:
  • public;
  • private;
  • protected;
  • если не указан ни один из этих трех типов, то уровень доступа определяется по умолчанию (default).

  • Первые два из них уже были рассмотрены. Последний уровень (доступ по умолчанию) упоминался в прошлой лекции – он допускает обращения из того же пакета, где объявлен и сам этот класс. По этой причине пакеты в Java являются не просто набором типов, а более структурированной единицей, так как типы внутри одного пакета могут больше взаимодействовать друг с другом, чем с типами из других пакетов.
    Наконец, protected дает доступ наследникам класса. Понятно, что наследникам может потребоваться доступ к некоторым элементам родителя, с которыми не приходится иметь дело внешним классам.
    Однако описанная структура не позволяет упорядочить модификаторы доступа так, чтобы каждый следующий строго расширял предыдущий. Модификатор protected может быть указан для наследника из другого пакета, а доступ по умолчанию допускает обращения из классов-ненаследников, если они находятся в том же пакете. По этой причине возможности protected были расширены таким образом, что он включает в себя доступ внутри пакета. Итак, модификаторы доступа упорядочиваются следующим образом (от менее открытых – к более открытым):
    private (none) default protected public
    Эта последовательность будет использована далее при изучении деталей наследования классов.
    Теперь рассмотрим, какие модификаторы доступа возможны для различных элементов языка.
  • Пакеты доступны всегда, поэтому у них нет модификаторов доступа (можно сказать, что все они public, то есть любой существующий в системе пакет может использоваться из любой точки программы).
  • Типы (классы и интерфейсы) верхнего уровня объявления. При их объявлении существует всего две возможности: указать модификатор public или не указывать его. Если доступ к типу является public, то это означает, что он доступен из любой точки кода. Если же он не public, то уровень доступа назначается по умолчанию: тип доступен только внутри того пакета, где он объявлен.
  • Массив имеет тот же уровень доступа, что и тип, на основе которого он объявлен (естественно, все примитивные типы являются полностью доступными).
  • Элементы и конструкторы объектных типов. Обладают всеми четырьмя возможными значениями уровня доступа. Все элементы интерфейсов являются public.


  • Для типов объявления верхнего уровня нет необходимости во всех четырех уровнях доступа. Private-типы образовывали бы закрытую мини-программу, никто не мог бы их использовать. Типы, доступные только для наследников, также не были признаны полезными.

    Разграничения доступа сказываются не только на обращении к элементам объектных типов или пакетов (через составное имя или прямое обращение), но также при вызове конструкторов, наследовании, приведении типов. Импортировать недоступные типы запрещается.

    Проверка уровня доступа проводится компилятором. Обратите внимание на следующие примеры:

    public class Wheel { private double radius; public double getRadius() { return radius; } }

    Значение поля radius недоступно снаружи класса, однако открытый метод getRadius() корректно возвращает его.

    Рассмотрим следующие два модуля компиляции:

    package first;

    // Некоторый класс Parent public class Parent { }

    package first;

    // Класс Child наследуется от класса Parent, // но имеет ограничение доступа по умолчанию class Child extends Parent { }

    public class Provider { public Parent getValue() { return new Child(); } }

    К методу getValue() класса Provider можно обратиться и из другого пакета, не только из пакета first, поскольку метод объявлен как public. Данный метод возвращает экземпляр класса Child, который недоступен из других пакетов. Однако следующий вызов является корректным:

    package second;

    import first.*;

    public class Test { public static void main(String s[]) { Provider pr = new Provider(); Parent p = pr.getValue(); System.out.println(p.getClass().getName()); // (Child)p - приведет к ошибке компиляции! } }

    Результатом будет:

    first.Child

    То есть на самом деле в классе Test работа идет с экземпляром недоступного класса Child, что возможно, поскольку обращение к нему делается через открытый класс Parent. Попытка же выполнить явное приведение вызовет ошибку. Да, тип объекта "угадан" верно, но доступ к закрытому типу всегда запрещен.

    Следующий пример:

    public class Point { private int x, y;

    public boolean equals(Object o) { if (o instanceof Point) { Point p = (Point)o; return p.x==x && p.y==y; } return false; } }

    В этом примере объявляется класс Point с двумя полями, описывающими координаты точки. Обратите внимание, что поля полностью закрыты – private. Далее попытаемся переопределить стандартный метод equals() таким образом, чтобы для аргументов, являющихся экземплярами класса Point, или его наследников (логика работы оператора instanceof), в случае равенства координат возвращалось истинное значение. Обратите внимание на строку, где делается сравнение координат,– для этого приходится обращаться к private-полям другого объекта!

    Тем не менее, такое действие корректно, поскольку private допускает обращения из любой точки класса, независимо от того, к какому именно объекту оно производится.

    Другие примеры разграничения доступа в Java будут рассматриваться по ходу курса.

    Создание переключателей с независимой фиксацией

    Создать переключатель с независимой фиксацией не сложнее, чем создать кнопку:
    Checkbox rdbox1; . . . public void init() { rdbox1 = new Checkbox("Switch 1"); add(rdbox1); }
    В этом фрагменте кода мы создаем переключатель chbox1 с названием Switch 1, а затем с помощью метода add добавляем его в контейнер, которым является окно аплета.
    Для определения текущего состояния переключателя вы можете использовать метод getState. Если переключатель включен, этот метод возвращает значение true, а если выключен - значение false.

    Создание переключателей с зависимой фиксацией

    Для каждой группы переключателей с зависимой фиксацией вы должны создать объект класса CheckboxGroup:

    Создание поля класса Label

    Текстовое поле класса Label создается вызовом соответствующего конструктора. Например, ниже мы создали текстовое поле, указав строку, которую надо в него записать:
    Label lbTextLabel; lbTextLabel = new Label("Выберите выравнивание");
    С помощью метода add вы можете добавить текстовое поле в окно аплета:
    add(lbTextLabel);
    Метод setAlignment позволяет при необходимости изменить выравнивание текста. Способ выравнивания необходимо указать через единственный параметр метода:
    lbTextLabel.setAlignment(Label.LEFT);
    При помощи метода setText вы сможете динамически изменять текст, расположенный в поле класса Label.
    Назад Вперед

    Создание поля класса LabelСоздание поля класса Label Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Создание поля класса Label
    printmenus();
    Создание поля класса LabelПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Создание поля класса Label»


    Создание поля класса LabelSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Создание поля класса Label»


    Создание поля класса LabelКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Создание поля класса Label»


    Создание поля класса LabelГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Создание поля класса Label»


    Создание поля класса LabelОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Создание поля класса Label»


    Создание поля класса LabelПроекты
    События
    Lab Downloads
    Посмотреть все Создание поля класса Label»


    Создание поля класса Label

    Создание поля TextArea

    Когда вы создаете многострочное текстовое поле редактирования, то можете использовать конструктор, допускающий указание размеров поля в строках и столбцах:
    TextArea txt; txt = new TextArea("Введите строку текста", 5, 35);
    Созданное поле добавляется в окно аплета методом add.
    Отметим, что в классе TextArea есть методы для работы с блоками текста (вставка и замена), а также методы, с помощью которых можно определить количество строк и столбцов в поле редактирования.
    Назад Вперед

    Создание поля TextAreaСоздание поля TextArea Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Создание поля TextArea
    printmenus();
    Создание поля TextAreaПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Создание поля TextArea»


    Создание поля TextAreaSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Создание поля TextArea»


    Создание поля TextAreaКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Создание поля TextArea»


    Создание поля TextAreaГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Создание поля TextArea»


    Создание поля TextAreaОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Создание поля TextArea»


    Создание поля TextAreaПроекты
    События
    Lab Downloads
    Посмотреть все Создание поля TextArea»


    Создание поля TextArea

    Создание списка класса List

    Процесс создания списка класса List несложен:
    List chBackgroundColor; chBackgroundColor = new List(6, false);
    При создании списка вы передаете конструктору количество одновременно отображаемых строк и флаг разрешения одновременного выбора нескольких строк. Если значение этого флага равно true, пользователь сможет выбирать из списка одновременно несколько строк, а если false - только одну строку.
    Для наполнения списка вы можете использовать уже знакомый вам метод addItem:
    chBackgroundColor.addItem("Yellow"); chBackgroundColor.addItem("Green"); chBackgroundColor.addItem("White");
    Список класса List добавляется к окну аплета методом add:
    add(chBackgroundColor);
    Кратко остановимся на нескольких методах класса List.
    Если вы разрешили пользователю выбирать из списка одновременно несколько элементов, то для получения ссылки на массив выбранных элементов вам пригодятся методы getSelectedItems и getSelectedIndexes:
    public String[] getSelectedItems(); public int[] getSelectedIndexes();
    С помощью метода setMultipleSelections вы можете динамически включать или выключать режим одновременного выбора нескольких элементов.
    В некоторых случаях вам может пригодиться метод clear, удаляющий все элементы из списка:
    public void clear();
    Методика использования других методов очевидна из краткого описания класса List, приведенного в нашей статье.

    Создание списков

    Конструктор класса Choice не имеет параметров. Создание списка с его помощью не вызовет у вас никаких затруднений:
    Choice chBackgroundColor; chBackgroundColor = new Choice();
    Для наполнения списка используйте метод addItem. В качестве параметра ему необходимо передать текстовую строку, которая будет связана с добавляемым элементом списка:
    chBackgroundColor.addItem("Yellow");
    Далее список можно добавить в окно аплета как компонент с помощью метода add:
    add(chBackgroundColor);
    Заметим, что список можно заполнять до или после добавления в окно аплета.
    После наполнения списка по умолчанию выделяется элемент, который был добавлен в список первым. При помощи метода select вы можете выделить любой элемент списка по его номеру или строке, связанной с элементом.
    Когд пользователь выбирает новую строку в списке, возникает событие. Обработчик этого события, реализованный, например, переопределением метода action, может получить номер выбранной строки при помощи метода getSelectedIndex. Пример обработки такого события вы найдете в разделе "Приложение ChoiceList".
    Если вас интересует не номер выбранного элемента, а строка, связанная с выбранным элементом, воспользуйтесь методом getSelectedItem.
    И, наконец, с помощью метода getItem вы можете получить текст строки, связанной с элементом, по номеру элемента.
    Назад Вперед

    Создание списковСоздание списков Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Создание списков
    printmenus();
    Создание списковПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Создание списков»


    Создание списковSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Создание списков»


    Создание списковКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Создание списков»


    Создание списковГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО

    Создание текстового поля класса TextField

    При создании текстового поля вы можете выбрать один из четырех конструкторов, соответственно, для создания поля без текста и без указания размера, без текста заданного размера, для создания поля с текстом и для создания поля с текстом указанного размера.
    Вот фрагмент кода, в котором создается поле с текстом, имеющее ширину, достаточную для размещения 35 символов:
    TextField txt; txt = new TextField( "Введите строку текста", 35);
    Созданное поле добавляется в окно аплета методом add.
    Большинство самых полезнных методов, необходимых для работы с полем класса TextField, определено в классе TextComponent, краткое описание которого мы привели ниже.

    Списки класса Choice

    Назад Вперед
    На базе класса Choice вы можете создать списки типа Drop Down или, как их еще называют, "выпадающие" списки. Такой список выглядит как текстовое поле высотой в одну строку, справа от которого располагается кнопка (рис. 3).
    Списки класса Choice
    Рис. 3. Список типа Drop Down, созданный на базе класса Choice
    Если нажать на эту кнопку, список раскроется и вы сможете сделать выбор из его элементов (рис. 4).
    Списки класса Choice
    Рис. 4. Раскрытый список, созданный на базе класса Choice
    В списке класса Choice одновременно можно выбрать только один элемент.

    Списки класса List

    Назад Вперед
    На базе класса List вы можете сделать список другого типа, который допускает выбор не только одного, но и нескольких элементов. В отличие от списка, созданного на базе класса Choice, список класса List может занимать прямоугольную область, в которой помещаются сразу несколько элементов. Этот список всегда находится в раскрытом состоянии (рис.5).
    Списки класса List
    Рис. 5. Список класса List, все элементы которого помещаются в окне списка
    Если размеры окна списка класса List недостаточны для того чтобы вместить в себя все элементы, в правой части окна списка автоматически создается полоса просмотра, с помощью которой можно пролистать весь список (рис. 6).
    Списки класса List
    Рис. 6. Список класса List с полосой просмотра

    Текстовое поле класса Label

    Назад Вперед
    На базе класса Label вы можете создать в окне аплета однострочное текстовое поле, которое не поддается редактированию. Основное назначение таких полей - подпись других компонент, таких, например, как группы переключателей или списки.

    Текстовое поле класса TextField

    Назад Вперед
    Для редактирования одной строки текста вы можете создать текстовое поле на базе класса TextField, которое несложно в использовании. Класс TextField создан на базе другого класса с именем TextComponent, поэтому при работе с текстовым полем класса TextField вы можете использовать и методы класса TextComponent.

    Тело класса

    Тело класса может содержать объявление элементов (members) класса:
  • полей;
  • внутренних типов (классов и интерфейсов);

  • и остальных допустимых конструкций:
  • конструкторов;

  • инициализаторов
  • статических инициализаторов.

  • Элементы класса имеют имена и передаются по наследству, не-элементы – нет. Для элементов простые имена указываются при объявлении, составные формируются из имени класса, или имени переменной объектного типа, и простого имени элемента. Областью видимости элементов является все объявление тела класса. Допускается применение любого из всех четырех модификаторов доступа. Напоминаем, что соглашения по именованию классов и их элементов обсуждались в прошлой лекции.
    Не-элементы не обладают именами, а потому не могут быть вызваны явно. Их вызывает сама виртуальная машина. Например, конструктор вызывается при создании объекта. По той же причине не-элементы не обладают модификаторами доступа.
    Элементами класса являются элементы, описанные в объявлении тела класса и переданные по наследству от класса-родителя (кроме Object – единственного класса, не имеющего родителя) и всех реализуемых интерфейсов при условии достаточного уровня доступа. Таким образом, если класс содержит элементы с доступом по умолчанию, то его наследники из разных пакетов будут обладать разным набором элементов. Классы из того же пакета могут пользоваться полным набором элементов, а из других пакетов – только protected и public. private-элементы по наследству не передаются.
    Поля и методы могут иметь одинаковые имена, поскольку обращение к полям всегда записывается без скобок, а к методам – всегда со скобками.
    Рассмотрим все эти конструкции более подробно.

    Объявление классов является центральной темой

    Объявление классов является центральной темой курса, поскольку любая программа на Java – это набор классов. Поскольку типы являются ключевой конструкцией языка, их структура довольно сложна, имеет много тонкостей. Поэтому данная тема разделена на две лекции.
    Эта лекция начинается с продолжения темы прошлой лекции – имена и доступ к именованным элементам языка. Необходимо рассмотреть механизм разграничения доступа в Java, как он устроен, для чего применяется. Затем будут описаны ключевые правила объявления классов.
    Лекция 8 подробно рассматривает особенности объектной модели Java. Вводится понятие интерфейса. Уточняются правила объявления классов и описывается объявление интерфейса.

    Заголовок класса

    Вначале указываются модификаторы класса. Модификаторы доступа для класса уже обсуждались. Допустимым является public, либо его отсутствие – доступ по умолчанию.
    Класс может быть объявлен как final. В этом случае не допускается создание наследников такого класса. На своей ветке наследования он является последним. Класс String и классы-обертки, например, представляют собой final-классы.
    После списка модификаторов указывается ключевое слово class, а затем имя класса – корректный Java-идентификатор. Таким образом, кратчайшим объявлением класса может быть такой модуль компиляции:
    class A {}
    Фигурные скобки обозначают тело класса, но о нем позже.
    Указанный идентификатор становится простым именем класса. Полное составное имя класса строится из полного составного имени пакета, в котором он объявлен (если это не безымянный пакет), и простого имени класса, разделенных точкой. Область видимости класса, где он может быть доступен по своему простому имени, – его пакет.
    Далее заголовок может содержать ключевое слово extends, после которого должно быть указано имя (простое или составное) доступного не-final класса. В этом случае объявляемый класс наследуется от указанного класса. Если выражение extends не применяется, то класс наследуется напрямую от Object. Выражение extends Object допускается и игнорируется.
    class Parent {} // = class Parent extends Object {}
    final class LastChild extends Parent {}
    // class WrongChild extends LastChild {} // ошибка!!
    Попытка расширить final-класс приведет к ошибке компиляции.
    Если в объявлении класса A указано выражение extends B, то класс A называют прямым наследником класса B.
    Класс A считается наследником класса B, если:

  • A является прямым наследником B;
  • существует класс C, который является наследником B, а A является наследником C (это правило применяется рекурсивно).

  • Таким образом можно проследить цепочки наследования на несколько уровней вверх.
    Если компилятор обнаруживает, что класс является своим наследником, возникает ошибка компиляции:
    // пример вызовет ошибку компиляции class A extends B {} class B extends C {} class C extends A {} // ошибка! Класс А стал своим наследником
    Далее в заголовке может быть указано ключевое слово implements, за которым должно следовать перечисление через запятую имен (простых или составных, повторения запрещены) доступных интерфейсов:
    public final class String implements Serializable, Comparable {}
    В этом случае говорят, что класс реализует перечисленные интерфейсы. Как видно из примера, класс может реализовывать любое количество интерфейсов. Если выражение implements отсутствует, то класс действительно не реализует никаких интерфейсов, здесь значений по умолчанию нет.
    Далее следует пара фигурных скобок, которые могут быть пустыми или содержать описание тела класса.

    В этой лекции началось рассмотрение

    В этой лекции началось рассмотрение ключевой конструкции языка Java – объявление класса.
    Первая тема посвящена средствам разграничения доступа. Главный вопрос – для чего этот механизм вводится в практически каждом современном языке высокого уровня. Необходимо понимать, что он предназначен не для обеспечения "безопасности" или "защиты" объекта от неких неправильных действий. Самая важная задача – разделить внешний интерфейс класса и детали его реализации с тем, чтобы в дальнейшем воспользоваться такими преимуществами ООП, как инкапсуляция и модульность.
    Затем были рассмотрены все четыре модификатора доступа, а также возможность их применения для различных элементов языка. Проверка уровня доступа выполняется уже во время компиляции и запрещает лишь явное использование типов. Например, с ними все же можно работать через их более открытых наследников.
    Объявление класса состоит из заголовка и тела класса. Формат заголовка был подробно описан. Для изучения тела класса необходимо вспомнить понятие элементов (members) класса. Ими могут быть поля, методы и внутренние типы. Для методов важным понятием является сигнатура.
    Кроме того, в теле класса объявляются конструкторы и инициализаторы. Поскольку они не являются элементами, к ним нельзя обратиться явно, они вызываются самой виртуальной машиной. Также конструкторы и инициализаторы не передаются по наследству.
    Дополнительно был рассмотрен метод main, который вызывается при старте виртуальной машины. Далее описываются тонкости, возникающие при передаче параметров, и связанный с этим вопрос о перегруженных методах.
    Классы Java мы продолжим рассматривать в следующих лекциях.

    Программирование на Java

    Аплет FormLayout

    Назад Вперед
    В окне аплета FormLayout (рис. 2) мы расположили те же самые органы управления, которые были использованы в предыдущем аплете FormDemo. Однако для указания способа размещения компонент мы выполнили настройку системы Layout Manager, выбрав режим GridLayout.

    Аплет FormLayout Рис. 2. Окно аплета FormLayout
    Для того чтобы увидеть рисунок в увеличенном виде, сделайте щелчок мышью по изображению

    И хотя пока еще внешний вид нашей формы оставляет желать лучшего, расположение отдельных компонент не изменяется при изменении размеров окна аплета.
    Проблема заключается в том, что в режиме GridLayout не удается управлять размерами компонент. Для устранения этого недостатка следует использовать режим GridBagLayout. Так как этот режим сложен для использования без визуального проектирования, мы отложим дальнейшее совершенстовование нашей формы до тех пор, пока не займемся изучением соответствующих средств Java WorkShop.

    Бинарное числовое расширение

    Это преобразование расширяет все примитивные числовые типы, кроме double, до типов int, long, float, double по правилам расширения примитивных типов. Бинарное числовое расширение происходит при числовых операторах, имеющих два аргумента, по следующим правилам:
  • если любой из аргументов имеет тип double, то и второй приводится к double;
  • иначе, если любой из аргументов имеет тип float, то и второй приводится к float;
  • иначе, если любой из аргументов имеет тип long, то и второй приводится к long;
  • иначе оба аргумента приводятся к int.

  • Бинарное числовое расширение может выполняться при следующих операциях:
  • арифметические операции +, -, *, /, %;
  • операции сравнения <, <=, >, >=, ==, !=;
  • битовые операции &, |, ^;
  • в некоторых случаях для операции с условием ? :.

  • Примеры работы всех этих операторов с учетом расширения подробно рассматривались в предыдущих лекциях.

    Числовое расширение

    Наконец, последний вид преобразований применяется при числовых операциях, когда требуется привести аргумент(ы) к типу длиной в 32 или 64 бита для проведения вычислений. Таким образом, при числовом расширении осуществляется только расширение примитивных типов.
    Различают унарное и бинарное числовое расширение.

    Исходный текст аплета FormLayout

    Исходный текст аплета FormLayout практически повторяет исходный текст аплета FormDemo, рассмотренный в нашей предыдущей статье. Единственное отличие заключается в том, что в методе init мы выполнили настройку системы Layout Manager, установив режим GridLayout:
    public void init() { setLayout(new GridLayout(4, 3)); . . . }
    Здесь для размещения компонент в окне аплета создается таблица из четырех строк и трех столбцов.
    Полный исходный текст аплета FormLayout вы найдете в листинге 1.
    Листинг 1. Файл FormLayout.java
    import java.applet.Applet; import java.awt.*; import java.util.*;
    public class FormLayout extends Applet { Button btReady;
    Checkbox chbox1; Checkbox chbox2;
    CheckboxGroup grRadio; Checkbox rd1; Checkbox rd2; Checkbox rd3;
    Choice ch1;
    Label lbFirstName; Label lbSecondName;
    TextField txtFirstName; TextField txtSecondName; TextArea txta;
    public void init() { setLayout(new GridLayout(4, 3));
    chbox1 = new Checkbox("First"); add(chbox1);
    lbFirstName = new Label( "Enter your first name:"); add(lbFirstName);
    txtFirstName = new TextField(" ", 30); add(txtFirstName);
    chbox2 = new Checkbox("Second"); add(chbox2);
    lbSecondName = new Label( "Enter your second name:"); add(lbSecondName);
    txtSecondName = new TextField(" ", 30); add(txtSecondName);
    grRadio = new CheckboxGroup(); rd1 = new Checkbox("Mode 1", grRadio, true); rd2 = new Checkbox("Mode 2", grRadio, false); rd3 = new Checkbox("Mode 3", grRadio, false);
    add(rd1); add(rd2); add(rd3);
    ch1 = new Choice(); ch1.addItem("White"); ch1.addItem("Green"); ch1.addItem("Yellow");
    add(ch1);
    setBackground(Color.yellow);
    lbFirstName.setBackground( Color.yellow); lbSecondName.setBackground( Color.yellow);
    rd1.setBackground(Color.yellow); rd2.setBackground(Color.yellow); rd3.setBackground(Color.yellow);
    chbox1.setBackground(Color.yellow); chbox2.setBackground(Color.yellow);
    txta = new TextArea("", 6, 45); add(txta); txta.setBackground(Color.white);

    btReady = new Button("Ready"); add(btReady); }

    public String getAppletInfo() { return "Name: FormDemo"; }

    public void paint(Graphics g) { Dimension dimAppWndDimension = getSize();

    g.setColor(Color.black); g.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1); }

    public boolean action(Event evt, Object obj) { Button btn; String str1, str2;

    if(evt.target instanceof Button) { if(evt.target.equals(btReady)) { btn = (Button)evt.target;

    str1 = txtFirstName.getText(); str2 = txtSecondName.getText();

    if(chbox1.getState()) txta.append(str1);

    if(chbox2.getState()) txta.append(str2);

    if(rd1.getState()) txta.append("\nMode 1\n");

    if(rd2.getState()) txta.append("\nMode 2\n");

    if(rd3.getState()) txta.append("\nMode 3\n"); } else { return false; } return true; } else if(evt.target instanceof Choice) { if(evt.target.equals(ch1)) { if(ch1.getSelectedIndex() == 0) txta.setBackground(Color.white);

    if(ch1.getSelectedIndex() == 1) txta.setBackground(Color.green);

    if(ch1.getSelectedIndex() == 2) txta.setBackground(Color.yellow); } } return false; } }

    Исходный текст документа HTML, созданный для нашего аплета системой Java WorkShop, представлен в листинге 2.

    Листинг 2. Файл FormLayout.tmp.html



    Назад Вперед

    Исходный текст аплета FormLayoutИсходный текст аплета FormLayout Контакты

    О компании

    Новости

    Вакансии

    Правовые аспекты

    Условия использования

    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.

    Исходный текст аплета FormLayout

    printmenus();

    Исходный текст аплета FormLayoutПрограммные продукты

    Рабочие станции и тонкие клиенты

    Серверы

    Системы хранения данных

    Посмотреть все Исходный текст аплета FormLayout»

    Исходный текст аплета FormLayoutSolaris 10

    Java 2 Standard Edition

    Developer Tools

    Top Downloads

    New Downloads

    Патчи и обновления

    Использование режима размещения CardLayout

    Как пользоваться режимом размещения CardLayout?
    Обычно в окне аплета создается две панели, одна из которых предназначена для показа страниц блокнота в режиме размещения CardLayout, а вторая содержит органы управления перелистыванием страниц, например, кнопки.
    Такие методы, как first, last, next и previous позволяют отображать, соответственно, первую, последнюю, следующую и предыдущую страницу блокнота. Если вызвать метод next при отображении последней страницы, в окне появится первая страница. Аналогично, при вызове метода previous для первой страницы блокнота вы увидите последнюю страницу.
    А как отобразить произвольную страницу, не перебирая их по одной методами next и previous?
    Для этого существует метод show. Учтите, что этот метод позволяет отображать только такие страницы, при добавлении которых методом add было указано имя, например:
    pCardPanel.add("BackgroundColor", pBackgroundColor); pCardPanel.add("ForegroundColor", pForegroundColor); pCardPanel.add("Font", pFont);
    Здесь в панель pCardPanel добавляются панели pBackgroundColor, pForegroundColor и pFont, имеющие имена, соответственно, "BackgroundColor", "ForegroundColor" и "Font".
    Назад Вперед

    Использование режима размещения CardLayoutИспользование режима размещения CardLayout Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Использование режима размещения CardLayout
    printmenus();
    Использование режима размещения CardLayoutПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Использование режима размещения CardLayout»


    Использование режима размещения CardLayoutSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Использование режима размещения CardLayout»


    Использование режима размещения CardLayoutКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Использование режима размещения CardLayout»


    Использование режима размещения CardLayoutГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Использование режима размещения CardLayout»


    Использование режима размещения CardLayoutОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Использование режима размещения CardLayout»


    Использование режима размещения CardLayoutПроекты
    События
    Lab Downloads
    Посмотреть все Использование режима размещения CardLayout»


    Использование режима размещения CardLayout

    Явное приведение

    Явное приведение уже многократно использовалось в примерах. При таком преобразовании слева от выражения, тип значения которого необходимо преобразовать, в круглых скобках указывается целевой тип. Если преобразование пройдет успешно, то результат будет точно указанного типа. Примеры:
    (byte)5 (Parent)new Child() (Flat)getCity().getStreet( ).getHouse().getFlat()
    Если комбинация типов образует запрещенное преобразование, возникает ошибка компиляции. Допускаются тождественные преобразования, расширения простых и объектных типов, сужения простых и объектных типов. Первые три всегда выполняются успешно. Последние два могут стать причиной ошибки исполнения, если значения оказались несовместимыми. Как следствие, выражение null всегда может быть успешно преобразовано к любому ссылочному типу. Но можно найти способ все-таки закодировать запрещенное преобразование.
    Child c=new Child(); // Child2 c2=(Child2)c; // запрещенное преобразование Parent p=c; // расширение Child2 c2=(Child2)p; // сужение
    Такой код будет успешно скомпилирован, однако, разумеется, при исполнении он всегда будет генерировать ошибку в последней строке. "Обманывать" компилятор смысла нет.

    Класс FlowLayout

    Ниже мы привели краткое описание класса FlowLayout:

    Конструкторы

    Без указания выравнивания и зазора между компонентами
    public FlowLayout();
    С указанием выравнивания
    public FlowLayout(int align);
    С указанием выравнивания и зазора между компонентами по вертикали и горизонтали
    public FlowLayout(int align, int hgap, int vgap);
    Обычно приложения не вызывают методы класса FlowLayout, устанавливая варианты компоновки при помощи конструкторов.
    Первый конструктор класса FlowLayout не имеет параметров. Он устанавливает по умолчанию режим центрирования компонент и зазор между компонентами по вертикали и горизонтали, равный 5 пикселам. Именно этот режим и использовался раньше во всех наших аплетах, так как именно он применяется по умолчанию объектами класса Panel, от которого наследуется класс Applet.
    С помощью второго конструктора вы можете выбрать режим размещения с заданным выравниванием компонент в окне контейнера по горизонтали. В качестве параметров этому конструктору необходимо передавать значения FlowLayout.LEFT, FlowLayout.RIGHT, или FlowLayout.CENTER. Зазор между компонентами будет при этом равен по умолчанию 5 пикселам.
    И, наконец, третий конструктор допускает раздельное указание режима выравнивания, а также зазоров между компонентами по вертикали и горизонтали в пикселах.

    Конструкторы класса BorderLayout

    Ниже приведено краткое описание конструкторов класса BorderLayout.
    public BorderLayout(); public BorderLayout(int hgap, int vgap);
    Эти конструкторы предназначены для создания схемы размещения, без зазора между компонентами и с зазором заданной величины,соответственно.

    Конструкторы класса CardLayout

    Режим без зазоров
    public CardLayout();
    Режим с зазорами по вертикали и горизонтали между компонентами и окном контейнера
    public CardLayout(int hgap, int vgap);

    Методы

  • addLayoutComponent

  • Не используется
    public void addLayoutComponent( String name, Component comp);
  • layoutContainer

  • Предназначен для того чтобы компоненты могли установить для себя предпочтительный размер
    public void layoutContainer( Container target);
  • minimumLayoutSize

  • Определение минимального размера окна контейнера, необходимого для размещения всех компонент
    public Dimension minimumLayoutSize( Container target);
  • preferredLayoutSize

  • Определение предпочтительного размера окна контейнера, необходимого для размещения всех компонент
    public Dimension preferredLayoutSize( Container target);
  • removeLayoutComponent

  • Удаление компоненты из контейнера
    public void removeLayoutComponent( Component comp);
  • toString

  • Получение строки названия метода компоновки
    public String toString();
    Назад Вперед

    МетодыМетоды Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Методы
    printmenus();
    МетодыПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Методы»


    МетодыSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Методы»


    МетодыКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Методы»


    МетодыГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Методы»


    МетодыОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Методы»


    МетодыПроекты
    События
    Lab Downloads
    Посмотреть все Методы»


    Методы

    Методы класса BorderLayout

    Перечислим также методы класса BorderLayout:
    public void addLayoutComponent( String name, Component comp); public void layoutContainer( Container target); public Dimension minimumLayoutSize( Container target); public Dimension preferredLayoutSize( Container target); public void removeLayoutComponent( Component comp); public String toString();

    Методы класса CardLayout

  • addLayoutComponent

  • Добавление компоненты с указанием имени
    public void addLayoutComponent( String name, Component comp);
  • first

  • Отображение первой страницы блокнота
    public void first(Container target);
  • last

  • Отображение последней страницы блокнота
    public void last(Container target);
  • next

  • Отображение следующей страницы блокнота
    public void next(Container target);
  • previous

  • Отображение предыдущей страницы блокнота
    public void previous(Container target);
  • layoutContainer

  • Выполнение размещения компонент
    public void layoutContainer( Container target);
  • minimumLayoutSize

  • Определение минимальных размеров окна, необходимых для размещения компонент
    public Dimension minimumLayoutSize( Container target);
  • preferredLayoutSize

  • Определение предпочтительных размеров окна, необходимых для размещения компонент
    public Dimension preferredLayoutSize( Container target);
  • removeLayoutComponent

  • Удаление заданной компоненты
    public void removeLayoutComponent( Component comp);
  • show

  • Отображение произвольной страницы блокнота по ее имени
    public void show( Container target, String name);
  • toString

  • Получение текстовой строки названия режима размещения
    public String toString();

    Оператор конкатенации строк

    Этот оператор уже рассматривался достаточно подробно. Если обоими его аргументами являются строки, то происходит обычная конкатенация. Если же тип String имеет лишь один из аргументов, то второй необходимо преобразовать в текст. Это единственная операция, при которой производится универсальное приведение любого значения к типу String.
    Это одно из свойств, выделяющих класс String из общего ряда.
    Правила преобразования уже были подробно описаны в этой лекции, а оператор конкатенации рассматривался в лекции "Типы данных".
    Небольшой пример:
    int i=1; double d=i/2.; String s="text"; print("i="+i+", d="+d+", s="+s);
    Результатом будет:
    i=1, d=0.5, s=text

    Поля

    Следующие три поля задают способы выравнивания:
  • CENTER

  • Центрирование
    public final static int CENTER;
  • LEFT

  • По левой границе
    public final static int LEFT;
  • RIGHT

  • По правой границе
    public final static int RIGHT;

    Преобразование к строке

    Это преобразование уже не раз упоминалось. Любой тип может быть приведен к строке, т.е. к экземпляру класса String. Такое преобразование является исключительным в силу того, что охватывает абсолютно все типы, в том числе и boolean, про который говорилось, что он не может участвовать ни в каком другом приведении, кроме тождественного.
    Напомним, как преобразуются различные типы.
  • Числовые типы записываются в текстовом виде без потери точности представления. Формально такое преобразование происходит в два этапа. Сначала на основе примитивного значения порождается экземпляр соответствующего класса-"обертки", а затем у него вызывается метод toString(). Но поскольку эти действия снаружи незаметны, многие JVM оптимизируют их и преобразуют примитивные значения в текст напрямую.
  • Булевская величина приводится к строке "true" или "false" в зависимости от значения.
  • Для объектных величин вызывается метод toString(). Если метод возвращает null, то результатом будет строка "null".
  • Для null-значения генерируется строка "null".


  • Преобразование примитивных типов (расширение и сужение)

    Очевидно, что следующие четыре вида приведений легко представляются в виде таблицы 7.1.

    Таблица 7.1. Виды приведений.
    простой тип, расширениессылочный тип, расширение
    простой тип, сужениессылочный тип, сужение

    Что все это означает? Начнем по порядку. Для простых типов расширение означает, что осуществляется переход от менее емкого типа к более емкому. Например, от типа byte (длина 1 байт) к типу int (длина 4 байта). Такие преобразования безопасны в том смысле, что новый тип всегда гарантированно вмещает в себя все данные, которые хранились в старом типе, и таким образом не происходит потери данных. Именно поэтому компилятор осуществляет его сам, незаметно для разработчика:
    byte b=3; int a=b;
    В последней строке значение переменной b типа byte будет преобразовано к типу переменной a (то есть, int) автоматически, никаких специальных действий для этого предпринимать не нужно.
    Следующие 19 преобразований являются расширяющими:
  • от byte к short, int, long, float, double
  • от short к int, long, float, double
  • от char к int, long, float, double
  • от int к long, float, double
  • от long к float, double
  • от float к double

  • Обратите внимание, что нельзя провести преобразование к типу char от типов меньшей или равной длины (byte, short), или, наоборот, к short от char без потери данных. Это связано с тем, что char, в отличие от остальных целочисленных типов, является беззнаковым.
    Тем не менее, следует помнить, что даже при расширении данные все-таки могут быть в особых случаях искажены. Они уже рассматривались в предыдущей лекции, это приведение значений int к типу float и приведение значений типа long к типу float или double. Хотя эти дробные типы вмещают гораздо большие числа, чем соответствующие целые, но у них меньше значащих разрядов.
    Повторим этот пример:
    long a=111111111111L; float f = a; a = (long) f; print(a);
    Результатом будет:
    111111110656
    Обратное преобразование - сужение - означает, что переход осуществляется от более емкого типа к менее емкому. При таком преобразовании есть риск потерять данные. Например, если число типа int было больше 127, то при приведении его к byte значения битов старше восьмого будут потеряны. В Java такое преобразование должно совершаться явным образом, т.е. программист в коде должен явно указать, что он намеревается осуществить такое преобразование и готов потерять данные.
    Следующие 23 преобразования являются сужающими:
  • от byte к char
  • от short к byte, char
  • от char к byte, short
  • от int к byte, short, char
  • от long к byte, short, char, int
  • от float к byte, short, char, int, long
  • от double к byte, short, char, int, long, float


  • Очевидно, что следующие четыре вида приведений легко представляются в виде таблицы 7.1.

    Таблица 7.1. Виды приведений.
    простой тип, расширениессылочный тип, расширение
    простой тип, сужениессылочный тип, сужение

    Что все это означает? Начнем по порядку. Для простых типов расширение означает, что осуществляется переход от менее емкого типа к более емкому. Например, от типа byte (длина 1 байт) к типу int (длина 4 байта). Такие преобразования безопасны в том смысле, что новый тип всегда гарантированно вмещает в себя все данные, которые хранились в старом типе, и таким образом не происходит потери данных. Именно поэтому компилятор осуществляет его сам, незаметно для разработчика:
    byte b=3; int a=b;
    В последней строке значение переменной b типа byte будет преобразовано к типу переменной a (то есть, int) автоматически, никаких специальных действий для этого предпринимать не нужно.
    Следующие 19 преобразований являются расширяющими:
  • от byte к short, int, long, float, double
  • от short к int, long, float, double
  • от char к int, long, float, double
  • от int к long, float, double
  • от long к float, double
  • от float к double

  • Обратите внимание, что нельзя провести преобразование к типу char от типов меньшей или равной длины (byte, short), или, наоборот, к short от char без потери данных. Это связано с тем, что char, в отличие от остальных целочисленных типов, является беззнаковым.
    Тем не менее, следует помнить, что даже при расширении данные все-таки могут быть в особых случаях искажены. Они уже рассматривались в предыдущей лекции, это приведение значений int к типу float и приведение значений типа long к типу float или double. Хотя эти дробные типы вмещают гораздо большие числа, чем соответствующие целые, но у них меньше значащих разрядов.
    Повторим этот пример:
    long a=111111111111L; float f = a; a = (long) f; print(a);
    Результатом будет:
    111111110656
    Обратное преобразование - сужение - означает, что переход осуществляется от более емкого типа к менее емкому. При таком преобразовании есть риск потерять данные. Например, если число типа int было больше 127, то при приведении его к byte значения битов старше восьмого будут потеряны. В Java такое преобразование должно совершаться явным образом, т.е. программист в коде должен явно указать, что он намеревается осуществить такое преобразование и готов потерять данные.
    Следующие 23 преобразования являются сужающими:
  • от byte к char
  • от short к byte, char
  • от char к byte, short
  • от int к byte, short, char
  • от long к byte, short, char, int
  • от float к byte, short, char, int, long
  • от double к byte, short, char, int, long, float



  • При сужении целочисленного типа к более узкому целочисленному все старшие биты, не попадающие в новый тип, просто отбрасываются. Не производится никакого округления или других действий для получения более корректного результата:

    print((byte)383); print((byte)384); print((byte)-384);

    Результатом будет:

    127 -128 -128

    Видно, что знаковый бит при сужении не оказал никакого влияния, так как был просто отброшен - результат приведения обратных чисел (384 и -384) оказался одинаковым. Следовательно, может быть потеряно не только точное абсолютное значение, но и знак величины.

    Это верно и для типа char:

    char c=40000; print((short)c);

    Результатом будет:

    -25536

    Сужение дробного типа до целочисленного является более сложной процедурой. Она проводится в два этапа.

    На первом шаге дробное значение преобразуется в long, если целевым типом является long, или в int - в противном случае (целевой тип byte, short, char или int). Для этого исходное дробное число сначала математически округляется в сторону нуля, то есть дробная часть просто отбрасывается.

    Например, число 3,84 будет округлено до 3, а -3,84 превратится в -3. При этом могут возникнуть особые случаи:

  • если исходное дробное значение является NaN, то результатом первого шага будет 0 выбранного типа (т.е. int или long);
  • если исходное дробное значение является положительной или отрицательной бесконечностью, то результатом первого шага будет, соответственно, максимально или минимально возможное значение для выбранного типа (т.е. для int или long);
  • наконец, если дробное значение было конечной величиной, но в результате округления получилось слишком большое по модулю число для выбранного типа (т.е. для int или long), то, как и в предыдущем пункте, результатом первого шага будет, соответственно, максимально или минимально возможное значение этого типа. Если же результат округления укладывается в диапазон значений выбранного типа, то он и будет результатом первого шага.


  • На втором шаге производится дальнейшее сужение от выбранного целочисленного типа к целевому, если таковое требуется, то есть может иметь место дополнительное преобразование от int к byte, short или char.

    Проиллюстрируем описанный алгоритм преобразованием от бесконечности ко всем целочисленным типам:

    Преобразование ссылочных типов (расширение и сужение)

    Переходим к ссылочным типам. Преобразование объектных типов лучше всего иллюстрируется с помощью дерева наследования. Рассмотрим небольшой пример наследования:
    // Объявляем класс Parent class Parent { int x; }
    // Объявляем класс Child и наследуем // его от класса Parent class Child extends Parent { int y; }
    // Объявляем второго наследника // класса Parent - класс Child2 class Child2 extends Parent { int z; }
    В каждом классе объявлено поле с уникальным именем. Будем рассматривать это поле как пример набора уникальных свойств, присущих некоторому объектному типу.
    Три объявленных класса могут порождать три вида объектов. Объекты класса Parent обладают только одним полем x, а значит, только ссылки типа Parent могут ссылаться на такие объекты. Объекты класса Child обладают полем y и полем x, полученным по наследству от класса Parent. Стало быть, на такие объекты могут указывать ссылки типа Child или Parent. Второй случай уже иллюстрировался следующим примером:
    Parent p = new Child();
    Обратите внимание, что с помощью такой ссылки p можно обращаться лишь к полю x созданного объекта. Поле y недоступно, так как компилятор, проверяя корректность выражения p.y, не может предугадать, что ссылка p будет указывать на объект типа Child во время исполнения программы. Он анализирует лишь тип самой переменной, а она объявлена как Parent, но в этом классе нет поля y, что и вызовет ошибку компиляции.
    Аналогично, объекты класса Child2 обладают полем z и полем x, полученным по наследству от класса Parent. Значит, на такие объекты могут указывать ссылки типа Child2 или Parent.
    Таким образом, ссылки типа Parent могут указывать на объект любого из трех рассматриваемых типов, а ссылки типа Child и Child2 - только на объекты точно такого же типа. Теперь можно перейти к преобразованию ссылочных типов на основе такого дерева наследования.
    Расширение означает переход от более конкретного типа к менее конкретному, т.е. переход от детей к родителям. В нашем примере преобразование от любого наследника (Child, Child2) к родителю (Parent) есть расширение, переход к более общему типу. Подобно случаю с примитивными типами, этот переход производится самой JVM при необходимости и незаметен для разработчика, то есть не требует никаких дополнительных усилий, так как он всегда проходит успешно: всегда можно обращаться к объекту, порожденному от наследника, по типу его родителя.

    Переходим к ссылочным типам. Преобразование объектных типов лучше всего иллюстрируется с помощью дерева наследования. Рассмотрим небольшой пример наследования:
    // Объявляем класс Parent class Parent { int x; }
    // Объявляем класс Child и наследуем // его от класса Parent class Child extends Parent { int y; }
    // Объявляем второго наследника // класса Parent - класс Child2 class Child2 extends Parent { int z; }
    В каждом классе объявлено поле с уникальным именем. Будем рассматривать это поле как пример набора уникальных свойств, присущих некоторому объектному типу.
    Три объявленных класса могут порождать три вида объектов. Объекты класса Parent обладают только одним полем x, а значит, только ссылки типа Parent могут ссылаться на такие объекты. Объекты класса Child обладают полем y и полем x, полученным по наследству от класса Parent. Стало быть, на такие объекты могут указывать ссылки типа Child или Parent. Второй случай уже иллюстрировался следующим примером:
    Parent p = new Child();
    Обратите внимание, что с помощью такой ссылки p можно обращаться лишь к полю x созданного объекта. Поле y недоступно, так как компилятор, проверяя корректность выражения p.y, не может предугадать, что ссылка p будет указывать на объект типа Child во время исполнения программы. Он анализирует лишь тип самой переменной, а она объявлена как Parent, но в этом классе нет поля y, что и вызовет ошибку компиляции.
    Аналогично, объекты класса Child2 обладают полем z и полем x, полученным по наследству от класса Parent. Значит, на такие объекты могут указывать ссылки типа Child2 или Parent.
    Таким образом, ссылки типа Parent могут указывать на объект любого из трех рассматриваемых типов, а ссылки типа Child и Child2 - только на объекты точно такого же типа. Теперь можно перейти к преобразованию ссылочных типов на основе такого дерева наследования.
    Расширение означает переход от более конкретного типа к менее конкретному, т.е. переход от детей к родителям. В нашем примере преобразование от любого наследника (Child, Child2) к родителю (Parent) есть расширение, переход к более общему типу. Подобно случаю с примитивными типами, этот переход производится самой JVM при необходимости и незаметен для разработчика, то есть не требует никаких дополнительных усилий, так как он всегда проходит успешно: всегда можно обращаться к объекту, порожденному от наследника, по типу его родителя.


    Parent p1=new Child(); Parent p2=new Child2();

    В обеих строках переменным типа Parent присваивается значение другого типа, а значит, происходит преобразование. Поскольку это расширение, оно производится автоматически и всегда успешно.

    Обратите внимание, что при подобном преобразовании с самим объектом ничего не происходит. Несмотря на то, что, например, поле y класса Child теперь недоступно, это не означает, что оно исчезло. Такое существенное изменение структуры объекта невозможно. Он был порожден от класса Child и сохраняет все его свойства. Изменился лишь тип ссылки, через которую идет обращение к объекту. Эту ситуацию можно условно сравнить с рассматриванием некоего предмета через подзорную трубу. Если перейти от трубы с большим увеличением к более слабой, то видимых деталей станет меньше, но сам предмет, конечно, никак от этого не изменится.

    Следующие преобразования являются расширяющими:

  • от класса A к классу B, если A наследуется от B (важным частным случаем является преобразование от любого ссылочного типа к Object);
  • от null-типа к любому объектному типу.


  • Второй случай иллюстрируется следующим примером:

    Parent p=null;

    Пустая ссылка null не обладает каким-либо конкретным ссылочным типом, поэтому иногда говорят о специальном null-типе. Однако на практике важно, что такое значение можно прозрачно преобразовать к любому объектному типу.

    С изучением остальных ссылочных типов (интерфейсов и массивов) этот список будет расширяться.

    Обратный переход, то есть движение по дереву наследования вниз, к наследникам, является сужением. Например, для рассматриваемого случая, переход от ссылки типа Parent, которая может ссылаться на объекты трех классов, к ссылке типа Child, которая может ссылаться на объекты лишь одного из трех классов, очевидно, является сужением. Такой переход может оказаться невозможным. Если ссылка типа Parent ссылается на объект типа Parent или Child2, то переход к Child невозможен, ведь в обоих случаях объект не обладает полем y, которое объявлено в классе Child. Поэтому при сужении разработчику необходимо явным образом указывать на то, что необходимо попытаться провести такое преобразование. JVM во время исполнения проверит корректность перехода. Если он возможен, преобразование будет проведено. Если же нет - возникнет ошибка.

    Применение класса BorderLayout

    Добавляя компоненты к контейнеру, вы должны использовать метод add с двумя параметрами, первый из которых указывает направление размещения, а второй - ссылку на добавляемый объект:
    add("North", btn1); add("East", btn2); add("West", btn3); add("South", btn4); add("Center", btn5);
    Назад Вперед

    Применение класса BorderLayoutПрименение класса BorderLayout Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Применение класса BorderLayout
    printmenus();
    Применение класса BorderLayoutПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Применение класса BorderLayout»


    Применение класса BorderLayoutSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Применение класса BorderLayout»


    Применение класса BorderLayoutКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Применение класса BorderLayout»


    Применение класса BorderLayoutГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Применение класса BorderLayout»


    Применение класса BorderLayoutОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Применение класса BorderLayout»


    Применение класса BorderLayoutПроекты
    События
    Lab Downloads
    Посмотреть все Применение класса BorderLayout»


    Применение класса BorderLayout

    Применение приведений

    Теперь, когда рассмотрены все виды преобразований, перейдем к ситуациям в коде, где могут встретиться или потребоваться приведения.
    Такие ситуации могут быть сгруппированы следующим образом.
  • Присвоение значений переменным (assignment). Не все переходы допустимы при таком преобразовании - ограничения выбраны таким образом, чтобы не могла возникнуть ошибочная ситуация.
  • Вызов метода. Это преобразование применяется к аргументам вызываемого метода или конструктора. Допускаются почти те же переходы, что и для присвоения значений. Такое приведение никогда не порождает ошибок. Так же приведение осуществляется при возвращении значения из метода.
  • Явное приведение. В этом случае явно указывается, к какому типу требуется привести исходное значение. Допускаются все виды преобразований, кроме приведений к строке и запрещенных. Может возникать ошибка времени исполнения программы.
  • Оператор конкатенации производит преобразование к строке своих аргументов.
  • Числовое расширение (numeric promotion). Числовые операции могут потребовать изменения типа аргумента(ов). Это преобразование имеет особое название - расширение (promotion), так как выбор целевого типа может зависеть не только от исходного значения, но и от второго аргумента операции.

  • Рассмотрим все случаи более подробно.

    Присвоение значений

    Такие ситуации неоднократно применялись в этой лекции для иллюстрации видов преобразования. Приведение может потребоваться, если переменной одного типа присваивается значение другого типа. Возможны следующие комбинации.
    Если сочетание этих двух типов образует запрещенное приведение, возникнет ошибка. Например, примитивные значения нельзя присваивать объектным переменным, включая следующие примеры:
    // пример вызовет ошибку компиляции
    // примитивное значение нельзя // присвоить объектной переменной Parent p = 3;
    // приведение к классу-"обертке" // также запрещено Long a=5L;
    // универсальное приведение к строке // возможно только для оператора + String s=true;
    Далее, если сочетание этих двух типов образует расширение (примитивных или ссылочных типов), то оно будет осуществлено автоматически, неявным для разработчика образом:
    int i=10; long a=i; Child c = new Child(); Parent p=c;
    Если же сочетание оказывается сужением, то возникает ошибка компиляции, такой переход не может быть проведен неявно:
    // пример вызовет ошибку компиляции int i=10; short s=i; // ошибка! сужение! Parent p = new Child(); Child c=p; // ошибка! сужение!
    Как уже упоминалось, в подобных случаях необходимо выполнять преобразование явно:
    int i=10; short s=(short)i; Parent p = new Child(); Child c=(Child)p;
    Более подробно явное сужение рассматривается ниже.
    Здесь может вызвать удивление следующая ситуация, которая не порождает ошибок компиляции:
    byte b=1; short s=2+3; char c=(byte)5+'a';
    В первой строке переменной типа byte присваивается значение целочисленного литерала типа int, что является сужением. Во второй строке переменной типа short присваивается результат сложения двух литералов типа int, а тип этой суммы также int. Наконец, в третьей строке переменной типа char присваивается результат сложения числа 5, приведенного к типу byte, и символьного литерала.
    Однако все эти примеры корректны. Для удобства разработчика компилятор проводит дополнительный анализ при присвоении значений переменным типа byte, short и char. Если таким переменным присваивается величина типа byte, short, char или int, причем ее значение может быть получено уже на момент компиляции, и оказывается, что это значение укладывается в диапазон типа переменной, то явного приведения не требуется. Если бы такой возможности не было, пришлось бы писать так:
    byte b=(byte)1; // преобразование необязательно short s=(short)(2+3); // преобразование необязательно char c=(char)((byte)5+'a'); // преобразование необязательно
    // преобразование необходимо, так как // число 200 не укладывается в тип byte byte b2=(byte)200;

    Работа с системой Layout Manager

    Назад Вперед
    В предыдущей статье мы рассказали вам о том, как создавать компоненты и размещать их в контейнере. Однако предложенный способ размещения компонент в окне контейнера едва ли можно назвать удобным, так как заранее трудно предугадать, на каком месте окажется тот или иной орган управления.
    К счастью, имеются способы, позволяющие контролировать размещение отдельных компонент в окне контейнера. И хотя эти способы не позволяют задавать конкретные координаты и размеры органов управления, использовнные схемы размещения компонент будут правильно работать на любой аппаратной платформе (не забывайте, что Java создавалась как средство разработки приложений, способных выполняться на любой платформе).
    В чем трудность создания пользовательского интерфейса для мультиплатформных систем?
    В том, что разработчик приложения никогда не знает характеристики устройства отображения, установленные у пользователя. Он, в частности, не может заранее знать разрешение монитора, размер системного шрифта и другие характеристики, необходимые для компоновки диалоговых панелей в терминах абсолютных координат.
    Средства пользовательского интерфейса AWT способны динамически измнять размеры компонент, подгоняя их "по месту" в системе пользователя. В результате значительно повышается вероятность того что внешний вид диалоговой панели, в каком она предстанет перед пользователем, будет похож на то, что ожидал разработчик.
    Назад Вперед

    Работа с системой Layout ManagerРабота с системой Layout Manager Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Работа с системой Layout Manager
    printmenus();
    Работа с системой Layout ManagerПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Работа с системой Layout Manager»


    Работа с системой Layout ManagerSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Работа с системой Layout Manager»


    Работа с системой Layout ManagerКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Работа с системой Layout Manager»


    Работа с системой Layout ManagerГарантийное обслуживание

    Режим BorderLayout

    Назад Вперед
    При использовании режима BorderLayout окно контейнера разделяется на рамку и центральную часть. При размещении компонент указывается направление от центра окна, в котором слудует размещать компоненты.

    Режим CardLayout

    Назад Вперед
    Режим CardLayout предназначен для создания набора диалоговых панелей, которые можно показывать по очереди в одном окне прямоугольной формы. Обычно для управления процессом перебора диалоговых панелей в режиме CardLayout используются отдельные органы управления, расположенные в другой панели или даже в другом аплете на той же самой странице сервера Web.
    Класс CardLayout содержит два конструктора и несколько методов.

    Режим FlowLayout

    Назад Вперед
    В этом режиме мы добавляли компоненты во всех примерах аплетов, приведенных ранее, так как по умолчанию для аплетов используется именно режим FlowLayout.

    Режим GridBagLayout

    Назад Вперед
    Режим GridBagLayout намного сложнее только что описанного режима GridLayout. Он позволяет размещать компоненты разного размера в таблице, задавая при этом для отдельных компонент размеры отступов и количество занимаемых ячеек.
    Сейчас мы не будем рассматривать этот режим, так как сходные результаты могут быть достигнуты другими, менее сложными способами. Например, вы можете создать в контейнере несколько панелей, использовав внутри каждой свой метод размещения компонент.
    Если вы создаете аплеты для размещения в документах HTML, никто не заставляет вас ограничиваться только одним аплетом для одного документа HTML - вы можете разместить там произвольное количество аплетов, организовав взаимодействие с одной стороны, между отдельными аплетами, а с другой - между аплетами и расширениями сервера Web.
    В интегрированной системе разработки приложений Java WorkShop версии 2.0 имеется встроенная система визуального проектирования пользовательского интерфейса, в результате работы которой создаются исходные тексты классов. Размещение органов управления при этом выполняется интерактивными средствами.
    Назад Вперед

    Режим GridBagLayoutРежим GridBagLayout Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Режим GridBagLayout
    printmenus();
    Режим GridBagLayoutПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Режим GridBagLayout»


    Режим GridBagLayoutSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Режим GridBagLayout»


    Режим GridBagLayoutКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Режим GridBagLayout»


    Режим GridBagLayoutГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Режим GridBagLayout»


    Режим GridBagLayoutОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Режим GridBagLayout»


    Режим GridBagLayoutПроекты
    События
    Lab Downloads
    Посмотреть все Режим GridBagLayout»


    Режим GridBagLayout

    Режим GridLayout

    Назад Вперед
    В режиме GridLayout компоненты размещаются в ячейках таблицы, параметры которой можно задать с помощью конструкторов класса GridLayout.
    При размещении компонент внутри ячеек таблицы все они получают одинаковые размеры. Если один из параметров, задающих размерность таблицы, равен нулю, это означает, что соответствующий столбец или строка может содержать любое количество элементов.
    Заметим, что оба параметра rows и cols не могут быть равны нулю одновременно.
    Приведем описание конструкторов класса GridLayout.

    Режимы системы Layout Manager

    Назад Вперед
    Прежде чем мы рассмотрим различные режимы компоновки системы Layout Manager, вспомним, как происходит наследование класса Applet (рис. 1).
    Режимы системы Layout Manager
    Рис. 1. Наследование класса Applet
    Класс Applet наследуется от класса Panel, который, в свою очередь, наследуется от класса Container и Component. Класс Container пользуется интерфейсом LayoutManager, что позволяет выбирать для контейнеров один из нескольких режимов размещения компонент в окне контейнера.
    Что же касается класса Panel, то для него по умолчанию выбирается режим размещения компонент с названием Flow Layout. Разумеется, вы можете выбрать другой режим размещения, указав его явным образом.
    Ниже мы перечислили все возможные режимы системы Layout Manager:

    Режим размещения компонент Описание
    FlowLayout Компоненты заполняют окно контейнера "потоком" по мере их добавления методом add. Они размещаются слева направо и сверху вниз
    GridLayout Компоненты размещаются в виде таблицы по мере добавления слева направо и сверху вниз. Для этой таблицы можно указать количество столбцов и строк
    GridBagLayout Аналогично предыдущему, однако при добавлении компонент в таблицу можно указать координаты ячейки, в которую помещается компонента
    BorderLayout При размещении компоненты указывается одно из нескольких направлений: юг, север, запад, восток, центр. Направление определяется относительно центра окна контейнера
    CardLayout Размещение компонент друг над другом в одном окне. Этот режим позволяет организовать набор диалоговых панелей в виде блокнота

    Каждому режиму соответсвует одноименный класс, методы и конструкторы которого позволяют выбирать различные компоновки.
    Далее на примере конкретного приложения мы рассмотрим использование перечисленных выше режимов системы Layout Manager.
    Назад Вперед

    Режимы системы Layout ManagerРежимы системы Layout Manager Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Режимы системы Layout Manager
    printmenus();
    Режимы системы Layout ManagerПрограммные продукты

    Тип переменной и тип ее значения

    Теперь, когда были подробно рассмотрены все примеры преобразований, нужно вернуться к вопросу переменной и ее значений.
    Как уже говорилось, переменная определяется тремя базовыми характеристиками: имя, тип, значение. Имя дается произвольным образом и никак не сказывается на свойствах переменной. А вот значение всегда имеет некоторый тип, не обязательно совпадающий с типом самой переменной. Поэтому необходимо рассмотреть все возможные типы переменных и выяснить, значения каких типов они могут иметь.
    Начнем с переменных примитивных типов. Поскольку эти переменные действительно хранят само значение, то их тип всегда точно совпадает с типом значения.
    Проиллюстрируем это правило на примере:
    byte b=3; char c='A'+3; long m=b+c; double d=m-3F ;
    Здесь переменная b будет хранить значение типа byte после сужения целочисленного литерала типа int. Переменная c будет хранить тип char после того, как компилятор осуществит сужающее преобразование результата суммирования, который будет иметь тип int. Для переменной m выполнится расширение результата суммирования типа от int к типу long. Наконец, переменная d будет хранить значение типа double, получившееся в результате расширения результата разности, который имеет тип float.
    Переходим к ссылочным типам. Во-первых, значение любой переменной такого типа - ссылка, которая может указывать лишь на объекты, порожденные от тех или иных классов, и далее обсуждаются только свойства данных классов. (Также объекты могут порождаться от массивов, эта тема рассматривается в отдельной лекции.)
    Кроме того, ссылочная переменная любого типа может иметь значение null. Большинство действий над такой переменной, например, обращение к полям или методам, приведет к ошибке.
    Итак, какова связь между типом ссылочной переменной и ее значением? Здесь главное ограничение - проверка компилятора, который следит, чтобы все действия, выполняющиеся над объектом, были корректны. Компилятор не может предугадать, на объект какого класса будет реально ссылаться та или иная переменная. Все, чем он располагает, - тип самой переменной. Именно его и использует компилятор для проверок. А значит, все допустимые значения переменной должны гарантированно обладать свойствами, определенными в классе-типе этой переменной. Такую гарантию дает только наследование. Отсюда получаем правило: ссылочная переменная типа A может указывать на объекты, порожденные от самого типа A или его наследников.

    Point p = new Point();

    В этом примере переменная и ее значение одинакового типа, поэтому над объектом можно совершать все возможные для данного класса действия.

    Parent p = new Child();

    Такое присвоение корректно, так как класс Child порожден от Parent. Однако теперь допустимые действия над переменной p, а значит, над объектом, только что созданным на основе класса Child, ограничены возможностями класса Parent. Например, если в классе Child определен некий новый метод newChildMethod(), то попытка его вызвать p.newChildMethod() будет порождать ошибку компиляции. Необходимо подчеркнуть, что никаких изменений с самим объектом не происходит, ограничение порождается используемым способом доступа к этому объекту - переменной типа Parent.

    Чтобы показать, что объект не потерял никаких свойств, произведем следующее обращение:

    ((Child)p).newChildMethod();

    Здесь в начале проводится явное сужение к типу Child. Во время исполнения программы JVM проверит, совместим ли тип объекта, на который ссылается переменная p, с типом Child. В нашем случае это именно так. В результате получается ссылка типа Child, поэтому становится допустимым вызов метода newChildMethod(), который вызывается у объекта, созданного в предыдущей строке.

    Обратим внимание на важный частный случай - переменная типа Object может ссылаться на объекты любого типа.

    В дальнейшем, с изучением новых типов (абстрактных классов, интерфейсов, массивов) этот список будет продолжаться, а пока коротко обобщим то, что было рассмотрено в данном разделе.

    Таблица 4.1. Целочисленные типы данных.Тип переменнойДопустимые типы ее значения
    ПримитивныйВ точности совпадает с типом переменной
    Ссылочный

  • null
  • совпадающий с типом переменной
  • классы-наследники от типа переменной


  • Object

  • null
  • любой ссылочный



  • Тождественное преобразование

    Самым простым является тождественное преобразование. В Java преобразование выражения любого типа к точно такому же типу всегда допустимо и успешно выполняется.
    Зачем нужно тождественное приведение? Есть две причины для того, чтобы выделить такое преобразование в особый вид.
    Во-первых, с теоретической точки зрения теперь можно утверждать, что любой тип в Java может участвовать в преобразовании, хотя бы в тождественном. Например, примитивный тип boolean нельзя привести ни к какому другому типу, кроме него самого.
    Во-вторых, иногда в Java могут встречаться такие выражения, как длинный последовательный вызов методов:
    print(getCity().getStreet().getHouse().getFlat().getRoom());
    При исполнении такого выражения сначала вызывается первый метод getCity(). Можно предположить, что возвращаемым значением будет объект класса City. У этого объекта далее будет вызван следующий метод getStreet(). Чтобы узнать, значение какого типа он вернет, необходимо посмотреть описание класса City. У этого значения будет вызван следующий метод (getHouse()), и так далее. Чтобы узнать результирующий тип всего выражения, необходимо просмотреть описание каждого метода и класса.
    Компилятор без труда справится с такой задачей, однако разработчику будет нелегко проследить всю цепочку. В этом случае можно воспользоваться тождественным преобразованием, выполнив приведение к точно такому же типу. Это ничего не изменит в структуре программы, но значительно облегчит чтение кода:
    print((MyFlatImpl)(getCity().getStreet().getHouse().getFlat()));

    Унарное числовое расширение

    Это преобразование расширяет примитивные типы byte, short или char до типов int по правилам расширения примитивных типов.
    Унарное числовое расширение может выполняться при следующих операциях:
  • унарные операции + и -;
  • битовое отрицание ~;
  • операции битового сдвига <<, >>, >>>.

  • Операторы сдвига имеют два аргумента, но они расширяются независимо друг от друга, поэтому данное преобразование является унарным. Таким образом, результат выражения 5<<3L имеет тип int. Вообще, результат операторов сдвига всегда имеет тип int или long.
    Примеры работы всех этих операторов с учетом расширения подробно рассматривались в предыдущих лекциях.

    Виды приведений

    В Java предусмотрено семь видов приведений:
  • тождественное (identity);
  • расширение примитивного типа (widening primitive);
  • сужение примитивного типа (narrowing primitive);
  • расширение объектного типа (widening reference);
  • сужение объектного типа (narrowing reference);
  • преобразование к строке (String);
  • запрещенные преобразования (forbidden).

  • Рассмотрим их по отдельности.

    Как уже говорилось, Java является

    Как уже говорилось, Java является строго типизированным языком, а это означает, что каждое выражение и каждая переменная имеет строго определенный тип уже на момент компиляции. Тип устанавливается на основе структуры применяемых выражений и типов литералов, переменных и методов, используемых в этих выражениях.
    Например:
    long a=3; a = 5+'A'+a; print("a="+Math.round(a/2F));
    Рассмотрим, как в этом примере компилятор устанавливает тип каждого выражения и какие преобразования (conversion) типов необходимо осуществить при каждом действии.
  • В первой строке литерал 3 имеет тип по умолчанию, то есть int. При присвоении этого значения переменной типа long необходимо провести преобразование.
  • Во второй строке сначала производится сложение значений типа int и char. Второй аргумент будет преобразован так, чтобы операция проводилась с точностью в 32 бита. Второй оператор сложения опять потребует преобразования, так как наличие переменной a увеличивает точность до 64 бит.
  • В третьей строке сначала будет выполнена операция деления, для чего значение long надо будет привести к типу float, так как второй операнд - дробный литерал. Результат будет передан в метод Math.round, который произведет математическое округление и вернет целочисленный результат типа int. Это значение необходимо преобразовать в текст, чтобы осуществить дальнейшую конкатенацию строк. Как будет показано ниже, эта операция проводится в два этапа - сначала простой тип приводится к объектному классу-"обертке" (в данном случае int к Integer), а затем у полученного объекта вызывается метод toString(), что дает преобразование к строке.

  • Данный пример показывает, что даже простые строки могут содержать многочисленные преобразования, зачастую незаметные для разработчика. Часто бывают и такие случаи, когда программисту необходимо явно изменить тип некоторого выражения или переменной, например, чтобы воспользоваться подходящим методом или конструктором.
    Вспомним уже рассмотренный пример:
    byte b=1; byte c=(byte)-b; int i=c;
    Здесь во второй строке необходимо провести явное преобразование, чтобы присвоить значение типа int переменной типа byte. В третьей же строке обратное приведение производится автоматически, неявным для разработчика образом.
    Рассмотрим сначала, какие переходы между различными типами можно осуществить.

    Вызов метода

    Это приведение возникает в случае, когда вызывается метод с объявленными параметрами одних типов, а при вызове передаются аргументы других типов. Объявление методов рассматривается в следующих лекциях курса, однако такой простой пример вполне понятен:
    // объявление метода с параметром типа long void calculate(long l) { ... }
    void main() { calculate(5); }
    Как видно, при вызове метода передается значение типа int, а не long, как определено в объявлении этого метода.
    Здесь компилятор предпринимает те же шаги, что и при приведении в процессе присвоения значений переменным. Если типы образуют запрещенное преобразование, возникнет ошибка.
    // пример вызовет ошибку компиляции
    void calculate(long a) { ... }
    void main() { calculate(new Long(5)); // здесь будет ошибка }
    Если сужение, то компилятор не сможет осуществить приведение и потребуются явные указания.
    void calculate(int a) { ... }
    void main() { long a=5; // calculate(a); // сужение! так будет ошибка. calculate((int)a); // корректный вызов }
    Наконец, в случае расширения, компилятор осуществит приведение сам, как и было показано в примере в начале этого раздела.
    Надо отметить, что, в отличие от ситуации присвоения, при вызове методов компилятор не производит преобразований примитивных значений от byte, short, char или int к byte, short или char. Это привело бы к усложнению работы с перегруженными методами. Например:
    // пример вызовет ошибку компиляции
    // объявляем перегруженные методы // с аргументами (byte, int) и (short, short) int m(byte a, int b) { return a+b; } int m(short a, short b) { return a-b; }
    void main() { print(m(12, 2)); // ошибка компиляции! }
    В этом примере компилятор выдаст ошибку, так как при вызове аргументы имеют тип (int, int), а метода с такими параметрами нет. Если бы компилятор проводил преобразование для целых величин, подобно ситуации с присвоением значений, то пример стал бы корректным, но пришлось бы прилагать дополнительные усилия, чтобы указать, какой из двух возможных перегруженных методов хотелось бы вызвать.
    Аналогичное преобразование потребуется при возвращении значения из метода, если тип результата и заявленный тип возвращаемого значения не совпадают.
    long get() { return 5; }
    Хотя в выражении return указан целочисленный литерал типа int, во всех местах, где будет вызван этот метод, будет получено значение типа long. Для такого преобразования действуют те же правила, что и для присвоения значения.
    В заключение рассмотрим пример, включающий в себя все рассмотренные случаи преобразования:
    short get(Parent p) { return 5+'A'; // приведение при возвращении значения }
    void main() { long a = // приведение при присвоении значения get(new Child()); // приведение при вызове метода }

    В этой лекции были рассмотрены

    В этой лекции были рассмотрены правила работы с типами данных в строго типизированном языке Java. Поскольку компилятор строго отслеживает тип каждой переменной и каждого выражения, в случае изменения этого типа необходимо четко понимать, какие действия допустимы, а какие нет, с точки зрения компилятора и виртуальной машины.
    Были рассмотрены все виды приведения типов в Java, то есть переход от одного типа к другому. Они разбиваются на 7 групп, начиная с тождественного и заканчивая запрещенными. Основные 4 вида определяются сужающими или расширяющими переходами между простыми или ссылочными типами. Важно помнить, что при явном сужении числовых типов старшие биты просто отбрасываются, что порой приводит к неожиданному результату. Что касается преобразования ссылочных значений, то здесь действует правило - преобразование никогда не порождает новых и не изменяет существующих объектов. Меняется лишь способ работы с ними.
    Особенным в Java является преобразование к строке.
    Затем были рассмотрены все ситуации в программе, где могут происходить преобразования типов. Прежде всего, это присвоение значений, когда преобразование зачастую происходит незаметно для программиста. Вызов метода во многом похож на инициализацию. Явное приведение позволяет осуществить желаемый переход в том случае, когда компилятор не позволяет сделать это неявно. Преобразование при выполнении числовых операций оказывает существенное влияние на результат.
    В заключение была рассмотрена связь между типом переменной и типом ее значения.

    Запрещенные преобразования

    Не все переходы между произвольными типами допустимы. Например, к запрещенным преобразованиям относятся: переходы от любого ссылочного типа к примитивному, от примитивного - к ссылочному (кроме преобразований к строке). Уже упоминавшийся пример - тип boolean - нельзя привести ни к какому другому типу, кроме boolean (как обычно - за исключением приведения к строке). Затем, невозможно привести друг к другу типы, находящиеся не на одной, а на соседних ветвях дерева наследования. В примере, который рассматривался для иллюстрации преобразований ссылочных типов, переход от Child к Child2 запрещен. В самом деле, ссылка типа Child может указывать на объекты, порожденные только от класса Child или его наследников. Это исключает вероятность того, что объект будет совместим с типом Child2.
    Этим список запрещенных преобразований не исчерпывается. Он довольно велик, и в то же время все варианты достаточно очевидны, поэтому подробно рассматриваться не будут. Желающие могут получить полную информацию из спецификации.
    Разумеется, попытка осуществить запрещенное преобразование вызовет ошибку компиляции.

    Программирование на Java

    Аплет Options

    Назад Вперед
    Аплет Options демонстрирует методики работы с панелями, а также с различными режимами системы Layout Manager.
    В окне аплета Options мы создали три панели (рис. 2).
    Аплет Options
    Рис. 2. Окно аплета Options
    В верхней панели отображается текстовая строка First panel. Цвет и шрифт этой строки, а также цвет фона можно задавать при помощи второй панели, расположенной в центре окна нашего аплета.
    Вторая панель представляет собой блокнот, на страницах которого находятся списки цвета фона, текста и шрифтов. С помощью кнопок нижней панели вы можете перелистывать страницы этого блокнота. На рис. 3 и 4 мы показали страницы, предназначенные для выбора цвета фона и цвета текста, соответственно.
    Аплет Options
    Рис. 3. Выбор цвета фона
    Аплет Options
    Рис. 4. Выбор цвета текста
    Нажимая кнопки Background Color, Foreground Color и Set Font, вы можете отображать нужные вам страницы блокнота. С помощью кнопок Next и Prev можно перебирать страницы блокнота в прямом или обратном направлении, соответственно.
    Назад Вперед

    Аплет OptionsАплет Options Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Аплет Options
    printmenus();
    Аплет OptionsПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Аплет Options»


    Аплет OptionsSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Аплет Options»


    Аплет OptionsКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Аплет Options»


    Аплет OptionsГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Аплет Options»


    Аплет OptionsОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Аплет Options»


    Аплет OptionsПроекты
    События
    Lab Downloads
    Посмотреть все Аплет Options»


    Аплет Options

    Добавление компонент в панели

    Назад Вперед
    Для добавления компонент в панель вы должны указать, для какой панели вызывается метод add, например:
    Botton btn1; Botton btn2; btn1 = new Button(); btn2 = new Button(); pBottomPanel.add(btn1); pBottomPanel.add(btn2);
    Назад Вперед

    Добавление компонент в панелиДобавление компонент в панели Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Добавление компонент в панели
    printmenus();
    Добавление компонент в панелиПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Добавление компонент в панели»


    Добавление компонент в панелиSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Добавление компонент в панели»


    Добавление компонент в панелиКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Добавление компонент в панели»


    Добавление компонент в панелиГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Добавление компонент в панели»


    Добавление компонент в панелиОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Добавление компонент в панели»


    Добавление компонент в панелиПроекты
    События
    Lab Downloads
    Посмотреть все Добавление компонент в панели»


    Добавление компонент в панели

    Добавление панелей

    Назад Вперед
    Создав панели, вы можете добавить их в окно аплета, вызвав метод add, как это показано ниже:
    add(pTopPanel); add(pBottomPanel);
    Заметим, что вы можете добавлять панели в панели, указывая, для какой панели нужно вызывать метод add:
    Panel pLeft; Panel pRight; pLeft = new Panel(); pRight = new Panel(); pTopPanel.setLayout(new GridLayout(1, 2)); pTopPanel.add(pLeft); pTopPanel.add(pRight);
    Здесь мы создали две панели pLeft и pRight, которые по нашему замыслу должны разделить пространство панели pTopPanel на две части по вертикали. Для обеспечения вертикального размещения панелей pLeft и pRight в панели pTopPanel мы вызвали для панели pTopPanel метод setLayout. При этом мы указали, что компоненты, добавляемые в эту панель, должны размещаться в таблице, состоящей из односй строки и двух столбцов.
    Затем панели pLeft и pRight были добавлены в панель pTopPanel методом add.
    Назад Вперед

    Добавление панелейДобавление панелей Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Добавление панелей
    printmenus();
    Добавление панелейПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Добавление панелей»


    Добавление панелейSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Добавление панелей»


    Добавление панелейКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Добавление панелей»


    Добавление панелейГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Добавление панелей»


    Добавление панелейОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Добавление панелей»


    Добавление панелейПроекты
    События
    Lab Downloads
    Посмотреть все Добавление панелей»


    Добавление панелей

    Главный класс аплета Options

    В главном классе аплета Options мы определили три поля с именами pPanel1, pCard и pControl:
    FirstPanel pPanel1; CardPanel pCard; ControlPanel pControl;
    В них хранятся ссылки на три класса, созданных нами для трех панелей.

    Интерфейсы

    Концепция абстрактных методов позволяет предложить альтернативу множественному наследованию. В Java класс может иметь только одного родителя, поскольку при множественном наследовании могут возникать конфликты, которые запутывают объектную модель. Например, если у класса есть два родителя, которые имеют одинаковый метод с различной реализацией, то какой из них унаследует новый класс? И какая будет функциональность родительского класса, который лишился своего метода?
    Все эти проблемы не возникают в том случае, если наследуются только абстрактные методы от нескольких родителей. Даже если унаследовано несколько одинаковых методов, все равно у них нет реализации и можно один раз описать тело метода, которое будет использоваться при вызове любого из этих методов.
    Именно так устроены интерфейсы в Java. От них нельзя порождать объекты, но другие классы могут реализовывать их.

    Исходный текст аплета Options

    Назад Вперед
    Исходный текст аплета Options представлен в листинге 1.
    Листинг 1. Файл Options.java
    import java.applet.*; import java.awt.*;
    public class Options extends Applet { FirstPanel pPanel1; CardPanel pCard; ControlPanel pControl;
    public String getAppletInfo() { return "Name: Options"; }
    public void init() { setLayout(new GridLayout(3, 1));
    pPanel1 = new FirstPanel(); add(pPanel1);
    pCard = new CardPanel(pPanel1); add(pCard);
    pControl = new ControlPanel(pCard); add(pControl);
    pPanel1.setBackground(Color.yellow); pPanel1.setForeground(Color.black);
    repaint(); } }
    class FirstPanel extends Panel { String szFontName = "TimesRoman";
    public void paint(Graphics g) { Dimension dimAppWndDimension = getSize();
    g.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);
    g.setFont(new Font(szFontName, Font.PLAIN, 24)); g.drawString("First panel", 10, 50);
    super.paint(g); } }
    class CardPanel extends Panel { Panel pBgColor; Panel pFgColor; Panel pFont;
    Panel pControlled;
    Choice chBgColor; Choice chFgColor; Choice chFont;
    Label lbBgColor; Label lbFgColor; Label lbFont;
    public CardPanel(Panel pControlledPanel) { pControlled = pControlledPanel;
    setLayout(new CardLayout(5, 5));
    pBgColor = new Panel(); pFgColor = new Panel(); pFont = new Panel();
    add("BgColor", pBgColor); add("FgColor", pFgColor); add("Font", pFont);
    chBgColor = new Choice(); chFgColor = new Choice(); chFont = new Choice();
    chBgColor.add("Yellow"); chBgColor.add("Green"); chBgColor.add("White");
    chFgColor.add("Black"); chFgColor.add("Red"); chFgColor.add("Green");
    chFont.add("TimesRoman"); chFont.add("Helvetica"); chFont.add("Courier");
    lbBgColor = new Label("Background color"); lbFgColor = new Label("Foreground color"); lbFont = new Label("Font");
    pBgColor.add(lbBgColor); pBgColor.add(chBgColor);

    pFgColor.add(lbFgColor); pFgColor.add(chFgColor);

    pFont.add(lbFont); pFont.add(chFont); }

    public void paint(Graphics g) { Dimension dimAppWndDimension = getSize(); g.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);

    super.paint(g); }

    public boolean action(Event evt, Object obj) { Choice ch;

    if(evt.target instanceof Choice) { ch = (Choice)evt.target;

    if(evt.target.equals(chBgColor)) { if(ch.getSelectedIndex() == 0) pControlled.setBackground( Color.yellow);

    else if(ch.getSelectedIndex() == 1) pControlled.setBackground( Color.green);

    else if(ch.getSelectedIndex() == 2) pControlled.setBackground( Color.white); } else if(evt.target.equals(chFgColor)) { if(ch.getSelectedIndex() == 0) pControlled.setForeground( Color.black);

    else if(ch.getSelectedIndex() == 1) pControlled.setForeground( Color.red);

    else if(ch.getSelectedIndex() == 2) pControlled.setForeground( Color.green); } else if(evt.target.equals(chFont)) { if(ch.getSelectedIndex() == 0) ((FirstPanel)pControlled).szFontName = "TimesRoman";

    else if(ch.getSelectedIndex() == 1) ((FirstPanel)pControlled).szFontName = "Helvetica";

    else if(ch.getSelectedIndex() == 2) ((FirstPanel)pControlled).szFontName = "Courier"; } else { return false; } pControlled.repaint();

    return true; } return false; } }

    class ControlPanel extends Panel { Button btNext; Button btPrev; Button btBgColor; Button btFgColor; Button btFont; Panel pCard;

    public ControlPanel(Panel pCardPanel) { pCard = pCardPanel; setLayout(new GridLayout(2,3));

    btBgColor = new Button("Background Color"); btFgColor = new Button("Foreground Color"); btFont = new Button("Set Font"); btNext = new Button("Next"); btPrev = new Button("Prev");

    add(btBgColor); add(btFgColor); add(btFont); add(btNext); add(btPrev); }

    public boolean action(Event evt, Object obj) { if(evt.target instanceof Button) { if(evt.target.equals(btBgColor)) { ((CardLayout)pCard.getLayout()).show( pCard, "BgColor"); } else if(evt.target.equals(btFgColor)) { ((CardLayout)pCard.getLayout()).show( pCard, "FgColor"); } else if(evt.target.equals(btFont)) { ((CardLayout)pCard.getLayout()).show( pCard, "Font"); } else if(evt.target.equals(btNext)) { ((CardLayout)pCard.getLayout()).next( pCard); } else if(evt.target.equals(btPrev)) { ((CardLayout)pCard.getLayout()). previous(pCard); } else { return false; } return true; } return false; } }

    Класс CardPanel

    С помощью класса CardPanel мы создали панель для блокнота, содержащего три страницы. Этот класс, так же как и предыдущий, создан на базе класса Panel.

    Класс ControlPanel

    Класс ControlPanel создан для нижней панели с управляющими кнопками.

    Класс FirstPanel

    Мы создали класс FirstPanel на базе класса Panel, определив в нем одно поле типа String и переопределив метод paint:
    class FirstPanel extends Panel { . . . }
    Текстовое поле szFontName хранит название шрифта, с использованием которого в окне верхней панели отображается текстовая строка:
    String szFontName = "TimesRoman";
    Метод paint определяет текущие размеры панели и рисует вокруг нее прямоугольную рамку:
    Dimension dimAppWndDimension = getSize();
    g.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);
    Далее метод paint выбирает в контекст отображения, связанный с панелью, шрифт с названием szFontName и рисует текстовую строку:
    g.setFont(new Font(szFontName, Font.PLAIN, 24)); g.drawString("First panel", 10, 50);
    Заметим, что сразу после запуска аплета рамка и строка будут нарисованы с использованием черного цвета, выбранного в контекст отображения панели по умолчанию. В дальнейшем вы можете изменить этот цвет при помощи соответствующей страницы блокнота, реализованного во второй панели.
    Последнее действие, которое выполняет метод paint первой панели - вызов метода paint из родительского класса:
    super.paint(g);
    Это приводит к перерисовке окна аплета.

    Ключевое слово abstract

    Следующее важное понятие, которое необходимо рассмотреть,– ключевое слово abstract.
    Иногда имеет смысл описать только заголовок метода, без его тела, и таким образом объявить, что данный метод будет существовать в этом классе. Реализацию этого метода, то есть его тело, можно описать позже.
    Рассмотрим пример. Предположим, необходимо создать набор графических элементов, неважно, каких именно. Например, они могут представлять собой геометрические фигуры – круг, квадрат, звезда и т.д.; или элементы пользовательского интерфейса – кнопки, поля ввода и т.д. Сейчас это не имеет решающего значения. Кроме того, существует специальный контейнер, который занимается их отрисовкой. Понятно, что внешний вид каждой компоненты уникален, а значит, соответствующий метод (назовем его paint()) будет реализован в разных элементах по-разному.
    Но в то же время у компонент может быть много общего. Например, любая из них занимает некоторую прямоугольную область контейнера. Сложные контуры фигуры необходимо вписать в прямоугольник, чтобы можно было анализировать перекрытия, проверять, не вылезает ли компонент за границы контейнера, и т.д. Каждая фигура может иметь цвет, которым ее надо рисовать, может быть видимой, или невидимой и т.д. Очевидно, что полезно создать родительский класс для всех компонент и один раз объявить в нем все общие свойства, чтобы каждая компонента лишь наследовала их.
    Но как поступить с методом отрисовки? Ведь родительский класс не представляет собой какую-либо фигуру, у него нет визуального представления. Можно объявить метод paint() в каждой компоненте независимо. Но тогда контейнер должен будет обладать сложной функциональностью, чтобы анализировать, какая именно компонента сейчас обрабатывается, выполнять приведение типа и только после этого вызывать нужный метод.
    Именно здесь удобно объявить абстрактный метод в родительском классе. У него нет внешнего вида, но известно, что он есть у каждого наследника. Поэтому заголовок метода описывается в родительском классе, тело метода у каждого наследника свое, а контейнер может спокойно пользоваться только базовым типом, не делая никаких приведений.
    Приведем упрощенный пример:

    // Базовая арифметическая операция abstract class Operation { public abstract int calculate(int a, int b); } // Сложение class Addition extends Operation { public int calculate(int a, int b) { return a+b; } }

    // Вычитание class Subtraction extends Operation { public int calculate(int a, int b) { return a-b; } }

    class Test { public static void main(String s[]) { Operation o1 = new Addition(); Operation o2 = new Subtraction();

    o1.calculate(2, 3); o2.calculate(3, 5); } }

    Видно, что выполнения операций сложения и вычитания в методе main() записываются одинаково.

    Обратите внимание – поскольку абстрактный метод не имеет тела, после описания его заголовка ставится точка с запятой. А раз у него нет тела, то к нему нельзя обращаться, пока его наследники не опишут реализацию. Это означает, что нельзя создавать экземпляры класса, у которого есть абстрактные методы. Такой класс сам объявляется абстрактным.

    Класс может быть абстрактным и в том случае, если у него нет абстрактных методов, но должен быть абстрактным, если такие методы есть. Разработчик может указать ключевое слово abstract в списке модификаторов класса, если хочет запретить создание экземпляров этого класса. Классы-наследники должны реализовать (implements) все абстрактные методы (если они есть) своего абстрактного родителя, чтобы их можно было объявлять неабстрактными и порождать от них экземпляры.

    Конечно, класс не может быть одновременно abstract и final. Это же верно и для методов. Кроме того, абстрактный метод не может быть private, native, static.

    Сам класс может без ограничений пользоваться своими абстрактными методами.

    abstract class Test { public abstract int getX(); public abstract int getY(); public double getLength() { return Math.sqrt(getX()*getX()+ getY()*getY()); } }

    Это корректно, поскольку метод getLength() может быть вызван только у объекта. Объект может быть порожден только от неабстрактного класса, который является наследником от Test, и должен был реализовать все абстрактные методы.

    По этой же причине можно объявлять переменные типа абстрактный класс. Они могут иметь значение null или ссылаться на объект, порожденный от неабстрактного наследника этого класса.

    Ключевые слова this и super

    Эти ключевые слова уже упоминались, рассматривались и некоторые случаи их применения. Здесь они будут описаны более подробно.
    Если выполнение кода происходит в динамическом контексте, то должен быть объект, ассоциированный с ним. В этом случае ключевое слово this возвращает ссылку на данный объект:
    class Test { public Object getThis() { return this; // Проверим, куда указывает эта ссылка } public static void main(String s[]) { Test t = new Test(); System.out.println(t.getThis()==t); // Сравнение } }
    Результатом работы программы будет:
    true
    То есть внутри методов слово this возвращает ссылку на объект, у которого этот метод вызван. Оно необходимо, если нужно передать аргумент, равный ссылке на данный объект, в какой-нибудь метод.
    class Human { public static void register(Human h) { System.out.println(h.name+ " is registered."); }
    private String name; public Human (String s) { name = s; register(this); // саморегистрация }
    public static void main(String s[]) { new Human("John"); } }
    Результатом будет:
    John is registered.
    Другое применение this рассматривалось в случае "затемняющих" объявлений:
    class Human { private String name;
    public void setName(String name) { this.name=name; } }
    Слово this можно использовать для обращения к полям, которые объявляются ниже:
    class Test { // int b=a; нельзя обращаться к // необъявленному полю! int b=this.a; int a=5; { System.out.println("a="+a+", b="+b); } public static void main(String s[]) { new Test(); } }
    Результатом работы программы будет:
    a=5, b=0
    Все происходит так же, как и для статических полей – b получает значение по умолчанию для a, т.е. ноль, а затем a инициализируется значением 5.
    Наконец, слово this применяется в конструкторах для явного вызова в первой строке другого конструктора этого же класса. Там же может применяться и слово super, только уже для обращения к конструктору родительского класса.
    Другие применения слова super также связаны с обращением к родительскому классу объекта. Например, оно может потребоваться в случае переопределения (overriding) родительского метода.

    Переопределением называют объявление метода, сигнатура которого совпадает с одним из методов родительского класса.

    class Parent { public int getValue() { return 5; } }

    class Child extends Parent { // Переопределение метода public int getValue() { return 3; }

    public static void main(String s[]) { Child c = new Child();

    // пример вызова переопределенного метода System.out.println(c.getValue()); } }

    Вызов переопределенного метода использует механизм полиморфизма, который подробно рассматривается в конце этой лекции. Однако ясно, что результатом выполнения примера будет значение 3. Невозможно, используя ссылку типа Child, получить из метода getValue() значение 5, родительский метод перекрыт и уже недоступен.

    Иногда при переопределении бывает полезно воспользоваться результатом работы родительского метода. Предположим, он делал сложные вычисления, а переопределенный метод должен вернуть округленный результат этих вычислений. Понятно, что гораздо удобнее обратиться к родительскому методу, чем заново описывать весь алгоритм. Здесь применяется слово super. Из класса наследника с его помощью можно обращаться к переопределенным методам родителя:

    class Parent { public int getValue() { return 5; } }

    class Child extends Parent {

    // переопределение метода public int getValue() { // обращение к методу родителя return super.getValue()+1; }

    public static void main(String s[]) { Child c = new Child(); System.out.println(c.getValue()); } }

    Результатом работы программы будет значение 6.

    Обращаться с помощью ключевого слова super к переопределенному методу родителя, т.е. на два уровня наследования вверх, невозможно. Если родительский класс переопределил функциональность своего родителя, значит, она не будет доступна его наследникам.

    Поскольку ключевые слова this и super требуют наличия ассоциированного объекта, т.е. динамического контекста, использование их в статическом контексте запрещено.

    Конструктор класса CardPanel

    При создании объекта класса CardPanel мы передаем конструктору ссылку на верхнюю панель, параметрами которой нужно управлять. Конструктор записывает эту ссылку в поле pControlled:
    public CardPanel(Panel pControlledPanel) { pControlled = pControlledPanel; . . . }
    Затем конструктор устанавливает режим размещения CardLayout, оставляя зазор по вертикали и горизонтали, равный пяти пикселам:
    setLayout(new CardLayout(5, 5));
    На следующем этапе мы создаем три панели для страниц блокнота и добавляем их в панель CardPanel, задавая имена:
    pBgColor = new Panel(); pFgColor = new Panel(); pFont = new Panel();
    add("BgColor", pBgColor); add("FgColor", pFgColor); add("Font", pFont);
    Теперь нам нужно создать и заполнить три списка, предназначенный для выбора цвета и шрифта. Эти списки создаются как объекты класса Choice:
    chBgColor = new Choice(); chFgColor = new Choice(); chFont = new Choice();
    После создания списки наполняются текстовыми строками. В каждый список мы добавляем по три строки:
    chBgColor.add("Yellow"); chBgColor.add("Green"); chBgColor.add("White");
    chFgColor.add("Black"); chFgColor.add("Red"); chFgColor.add("Green");
    chFont.add("TimesRoman"); chFont.add("Helvetica"); chFont.add("Courier");
    Для того чтобы снабдить списки подписями, мы создаем три объекта класса Label:
    lbBgColor = new Label("Background color"); lbFgColor = new Label("Foreground color"); lbFont = new Label("Font");
    Эти объекты, а также списки добавляются на свои страницы блокнота (то есть в свои панели):
    pBgColor.add(lbBgColor); pBgColor.add(chBgColor);
    pFgColor.add(lbFgColor); pFgColor.add(chFgColor);
    pFont.add(lbFont); pFont.add(chFont);
    На этом работа метода init заканчивается.

    Конструктор класса ControlPanel

    В задачу конструктора класса ControlPanel входит запоминание ссылки на панель блокнота, установка режима размещения компонент GridLayout, а также создание и добавление в нижнюю панель управляющих кнопок:
    public ControlPanel(Panel pCardPanel) { pCard = pCardPanel; setLayout(new GridLayout(2,3));
    btBgColor = new Button("Background Color"); btFgColor = new Button("Foreground Color"); btFont = new Button("Set Font"); btNext = new Button("Next"); btPrev = new Button("Prev");
    add(btBgColor); add(btFgColor); add(btFont); add(btNext); add(btPrev); }
    Кнопки располагаются в ячейках таблицы, содержащей две строки и три столбца. В целом конструктор класса ControlPanel не имеет никаких интересных особенностей.

    Метод action

    Метод action обрабатывает события, возникающие в результате выбора новых значений из списков, расположенных на страницах блокнота. Схема обработки событий не имеет никаких особенностей.
    Вначале метод action проверяет, что событие вызвано списком класса Choice:
    if(evt.target instanceof Choice) { . . . return true; } return false; }
    События, связанные с изменением цвета фона, обрабатываются следующим образом:
    ch = (Choice)evt.target;
    if(evt.target.equals(chBgColor)) { if(ch.getSelectedIndex() == 0) pControlled.setBackground( Color.yellow);
    else if(ch.getSelectedIndex() == 1) pControlled.setBackground( Color.green);
    else if(ch.getSelectedIndex() == 2) pControlled.setBackground( Color.white); }
    Здесь метод setBackground вызывается для объекта, ссылка на который передана конструктору класса и записана в поле pControlled. Это ссылка на панель, размещенную в верхней части окна нашего аплета.
    Аналогичным образом изменяется цвет текста и рамки для верхней панели:
    else if(evt.target.equals(chFgColor)) { if(ch.getSelectedIndex() == 0) pControlled.setForeground( Color.black);
    else if(ch.getSelectedIndex() == 1) pControlled.setForeground( Color.red);
    else if(ch.getSelectedIndex() == 2) pControlled.setForeground( Color.green); }
    Для изменения шрифта мы устанавливаем новое значение переменной поля szFontName, определенной в классе FirstPanel:
    else if(evt.target.equals(chFont)) { if(ch.getSelectedIndex() == 0) ((FirstPanel)pControlled).szFontName = "TimesRoman";
    else if(ch.getSelectedIndex() == 1) ((FirstPanel)pControlled).szFontName = "Helvetica";
    else if(ch.getSelectedIndex() == 2) ((FirstPanel)pControlled).szFontName = "Courier"; }
    Для того чтобы адресоваться к полю szFontName, нам пришлось выполнить явное преобразование типа ссылки pControlled.
    Последнее действие, которое совершает метод action - это перерисовка окна верхней панели, которая выполняется с помощью метода repaint:
    pControlled.repaint();

    Метод init

    Прежде всего метод init устанавливает для окна аплета режим размещения GridLayout:
    setLayout(new GridLayout(3, 1));
    Окно аплета делится на три горизнтальные области, в которых мы будем размещать панели.
    Панели создаются с помощью оператора new как объекты соответствующих классов, определенных в нашем приложении:
    pPanel1 = new FirstPanel(); add(pPanel1);
    pCard = new CardPanel(pPanel1); add(pCard);
    pControl = new ControlPanel(pCard); add(pControl);
    Для добавления панелей в окно аплета мы использовали метод add.
    Далее метод init устанавливает начальные значения для цвета фона и текста верхней панели:
    pPanel1.setBackground(Color.yellow); pPanel1.setForeground(Color.black);
    Обратите внимание, что мы вызываем методы setBackground и setForeground для объекта pPanel1.
    После выполнения всех этих действий метод init перерисовывает окно аплета, вызывая метод repaint:
    repaint();

    Методы

    Рассмотрим случай переопределения (overriding) методов:
    class Parent { public int getValue() { return 0; } } class Child extends Parent { public int getValue() { return 1; } }
    И строки, демонстрирующие работу с этими методами:
    Child c = new Child(); System.out.println(c.getValue()); Parent p = c; System.out.println(p.getValue());
    Результатом будет:
    1 1
    Можно видеть, что родительский метод полностью перекрыт, значение 0 никак нельзя получить через ссылку, указывающую на объект класса Child. В этом ключевая особенность полиморфизма – наследники могут изменять родительское поведение, даже если обращение к ним производится по ссылке родительского типа. Напомним, что, хотя старый метод снаружи уже недоступен, внутри класса-наследника к нему все же можно обратиться с помощью super.
    Рассмотрим более сложный пример:
    class Parent { public int getValue() { return 0; } public void print() { System.out.println(getValue()); } }
    class Child extends Parent { public int getValue() { return 1; } }
    Что появится на консоли после выполнения следующих строк?
    Parent p = new Child(); p.print();
    С помощью ссылки типа Parent вызывается метод print(), объявленный в классе Parent. Из этого метода делается обращение к getValue(), которое в классе Parent возвращает 0. Но компилятор уже не может предсказать, к динамическому методу какого класса произойдет обращение во время работы программы. Это определяет виртуальная машина на основе объекта, на который указывает ссылка. И раз этот объект порожден от Child, то существует лишь один метод getValue().
    Результатом работы примера будет:
    1
    Данный пример демонстрирует, что переопределение методов должно производиться с осторожностью. Если слишком сильно изменить логику их работы, нарушить принятые соглашения (например, начать возвращать null в качестве значения ссылочного типа, если родительский метод такого не допускал), это может привести к сбоям в работе родительского класса, а значит, объекта наследника. Более того, существуют и некоторые обязательные ограничения.
    Вспомним, что заголовок метода состоит из модификаторов, возвращаемого значения, сигнатуры и throws-выражения. Сигнатура (имя и набор аргументов) остается неизменной, если говорить о переопределении. Возвращаемое значение также не может меняться, иначе это приведет к появлению двух разных методов с одинаковыми сигнатурами.
    Рассмотрим модификаторы доступа.

    Рассмотрим случай переопределения (overriding) методов:
    class Parent { public int getValue() { return 0; } } class Child extends Parent { public int getValue() { return 1; } }
    И строки, демонстрирующие работу с этими методами:
    Child c = new Child(); System.out.println(c.getValue()); Parent p = c; System.out.println(p.getValue());
    Результатом будет:
    1 1
    Можно видеть, что родительский метод полностью перекрыт, значение 0 никак нельзя получить через ссылку, указывающую на объект класса Child. В этом ключевая особенность полиморфизма – наследники могут изменять родительское поведение, даже если обращение к ним производится по ссылке родительского типа. Напомним, что, хотя старый метод снаружи уже недоступен, внутри класса-наследника к нему все же можно обратиться с помощью super.
    Рассмотрим более сложный пример:
    class Parent { public int getValue() { return 0; } public void print() { System.out.println(getValue()); } }
    class Child extends Parent { public int getValue() { return 1; } }
    Что появится на консоли после выполнения следующих строк?
    Parent p = new Child(); p.print();
    С помощью ссылки типа Parent вызывается метод print(), объявленный в классе Parent. Из этого метода делается обращение к getValue(), которое в классе Parent возвращает 0. Но компилятор уже не может предсказать, к динамическому методу какого класса произойдет обращение во время работы программы. Это определяет виртуальная машина на основе объекта, на который указывает ссылка. И раз этот объект порожден от Child, то существует лишь один метод getValue().
    Результатом работы примера будет:
    1
    Данный пример демонстрирует, что переопределение методов должно производиться с осторожностью. Если слишком сильно изменить логику их работы, нарушить принятые соглашения (например, начать возвращать null в качестве значения ссылочного типа, если родительский метод такого не допускал), это может привести к сбоям в работе родительского класса, а значит, объекта наследника. Более того, существуют и некоторые обязательные ограничения.
    Вспомним, что заголовок метода состоит из модификаторов, возвращаемого значения, сигнатуры и throws-выражения. Сигнатура (имя и набор аргументов) остается неизменной, если говорить о переопределении. Возвращаемое значение также не может меняться, иначе это приведет к появлению двух разных методов с одинаковыми сигнатурами.
    Рассмотрим модификаторы доступа.

    Объявление интерфейсов

    Объявление интерфейсов очень похоже на упрощенное объявление классов.
    Оно начинается с заголовка. Сначала указываются модификаторы. Интерфейс может быть объявлен как public и тогда он будет доступен для общего использования, либо модификатор доступа может не указываться, в этом случае интерфейс доступен только для типов своего пакета. Модификатор abstract для интерфейса не требуется, поскольку все интерфейсы являются абстрактными. Его можно указать, но делать этого не рекомендуется, чтобы не загромождать код.
    Далее записывается ключевое слово interface и имя интерфейса.
    После этого может следовать ключевое слово extends и список интерфейсов, от которых будет наследоваться объявляемый интерфейс. Родительских типов может быть много, главное, чтобы не было повторений и чтобы отношение наследования не образовывало циклической зависимости.
    Наследование интерфейсов действительно очень гибкое. Так, если есть два интерфейса, A и B, причем B наследуется от A, то новый интерфейс C может наследоваться от них обоих. Впрочем, понятно, что указание наследования от A является избыточным, все элементы этого интерфейса и так будут получены по наследству через интерфейс B.
    Затем в фигурных скобках записывается тело интерфейса.
    public interface Drawble extends Colorable, Resizable { }
    Тело интерфейса состоит из объявления элементов, то есть полей-констант и абстрактных методов. Все поля интерфейса должны быть public final static, так что эти модификаторы указывать необязательно и даже нежелательно, чтобы не загромождать код. Поскольку поля объявляются финальными, необходимо их сразу инициализировать.
    public interface Directions { int RIGHT=1; int LEFT=2; int UP=3; int DOWN=4; }
    Все методы интерфейса являются public abstract и эти модификаторы также необязательны.
    public interface Moveable { void moveRight(); void moveLeft(); void moveUp(); void moveDown(); }
    Как мы видим, описание интерфейса гораздо проще, чем объявление класса.

    Описание исходного текста аплета Options

    Назад Вперед
    Помимо основного класса Options в нашем аплете создается еще три класса для панелей с именами FirstPanel, CardPanel и ControlPanel.
    Класс FirstPanel соответствует самой верхней панели, в которой отображается строка текста First panel. Классы CardPanel и ControlPanel испльзуются для создания панелей со списками и управляющими кнопками, соответственно. Мы будем рассматривать эти классы по отдельности.

    Полиморфизм

    Ранее были рассмотрены правила объявления классов с учетом их наследования. В этой лекции было введено понятие переопределенного метода. Однако полиморфизм требует более глубокого изучения. При объявлении одноименных полей или методов с совпадающими сигнатурами происходит перекрытие элементов из родительского и наследующего класса. Рассмотрим, как функционируют классы и объекты в таких ситуациях.

    Полиморфизм и объекты

    В заключение рассмотрим несколько особенностей, вытекающих из свойств полиморфизма.
    Во-первых, теперь можно точно сформулировать, что является элементами ссылочного типа. Ссылочный тип обладает следующими элементами:
  • непосредственно объявленными в его теле;
  • объявленными в его родительском классе и реализуемых интерфейсах, кроме:

  • private-элементов;
  • "скрытых" элементов (полей и статических методов, скрытых одноименными элементами);
  • переопределенных (динамических) методов.

  • Во-вторых, продолжим рассматривать взаимосвязь типа переменной и типов ее возможных значений. К случаям, описанным в предыдущей лекции, добавляются еще два. Переменная типа абстрактный класс может ссылаться на объекты, порожденные неабстрактным наследником этого класса. Переменная типа интерфейс может ссылаться на объекты, порожденные от класса, реализующего данный интерфейс.
    Сведем эти данные в таблицу.

    Таблица 8.1. Взаимосвязь типа переменной и типов ее возможных значений.Тип переменнойДопустимые типы ее значения
    Абстрактный класс

  • null
  • неабстрактный наследник


  • Интерфейс


  • null
  • классы, реализующие интерфейс, а именно:

  • реализующие напрямую (заголовок содержит implements);
  • наследуемые от реализующих классов;
  • реализующие наследников этого интерфейса;
  • смешанный случай - наследование от класса, реализующего наследника интерфейса


  • Таким образом, Java предоставляет гибкую и мощную модель объектов, позволяющую проектировать самые сложные системы. Необходимо хорошо разбираться в ее основных свойствах и механизмах – наследование, статические элементы, абстрактные элементы, интерфейсы, полиморфизм, разграничения доступа и другие. Все они позволяют избегать дублирующего кода, облегчают развитие системы, добавление новых возможностей и изменение старых, помогают обеспечивать минимальную связность между частями системы, то есть повышают модульность. Также удачные технические решения можно многократно использовать в различных системах, сокращая и упрощая процесс их создания.
    Для достижения таких важных целей требуется не только знание Java, но и владение объектно-ориентированным подходом, основными способами проектирования систем и проверки качества архитектурных решений. Платформа Java является основой и весьма удобным инструментом для применения всех этих технологий.

    Поля

    Начнем с полей, которые могут быть статическими или динамическими. Рассмотрим пример:
    class Parent { int a=2; } class Child extends Parent { int a=3; }
    Прежде всего, нужно сказать, что такое объявление корректно. Наследники могут объявлять поля с любыми именами, даже совпадающими с родительскими. Затем, необходимо понять, как два одноименных поля будут сосуществовать. Действительно, объекты класса Child будут содержать сразу две переменных, а поскольку они могут отличаться не только значением, но и типом (ведь это два независимых поля), именно компилятор будет определять, какое из значений использовать. Компилятор может опираться только на тип ссылки, с помощью которой происходит обращение к полю:
    Child c = new Child(); System.out.println(c.a); Parent p = c; System.out.println(p.a);
    Обе ссылки указывают на один и тот же объект, порожденный от класса Child, но одна из них имеет такой же тип, а другая – Parent. Отсюда следуют и результаты:
    3 2
    Объявление поля в классе-наследнике "скрыло" родительское поле. Данное объявление так и называется – "скрывающим" (hiding). Это особый случай перекрытия областей видимости, отличный от "затеняющего" (shadowing) и "заслоняющего" (obscuring) объявлений. Тем не менее, родительское поле продолжает существовать. К нему можно обратиться и явно:
    class Child extends Parent { int a=3; // скрывающее объявление int b=((Parent)this).a; // более громоздкое объявление int c=super.a; // более простое }
    Переменные b и c получат значение, хранящееся в родительском поле a. Хотя выражение с super более простое, оно не позволит обратиться на два уровня вверх по дереву наследования. А ведь вполне возможно, что в родительском классе это поле также было скрывающим и в родителе родителя хранится еще одно значение. К нему можно обратиться явным приведением, как это делается для b.
    Рассмотрим следующий пример:
    class Parent { int x=0; public void printX() { System.out.println(x); } } class Child extends Parent { int x=-1; }

    Каков будет результат следующих строк?

    new Child().printX();

    Значение какого поля будет распечатано? Метод вызывается с помощью ссылки типа Child, но это не сыграет никакой роли. Вызывается метод, определенный в классе Parent, и компилятор, конечно, расценил обращение к полю x в этом методе именно как к полю класса Parent. Поэтому результатом будет 0.

    Перейдем к статическим полям. На самом деле, для них проблем и конфликтов, связанных с полиморфизмом, не существует.

    Рассмотрим пример:

    class Parent { static int a=2; } class Child extends Parent { static int a=3; }

    Каков будет результат следующих строк?

    Child c = new Child(); System.out.println(c.a); Parent p = c; System.out.println(p.a);

    Нужно вспомнить, как компилятор обрабатывает обращения к статическим полям через ссылочные значения. Неважно, на какой объект указывает ссылка. Более того, она может быть даже равна null. Все определяется типом ссылки.

    Поэтому рассматриваемый пример эквивалентен:

    System.out.println(Child.a) System.out.println(Parent.a)

    А его результат сомнений уже не вызывает:

    3 2

    Можно привести следующее пояснение. Статическое поле принадлежит классу, а не объекту. В результате появление классов-наследников со скрывающими (hiding) объявлениями никак не сказывается на работе с исходным полем. Компилятор всегда может определить, через ссылку какого типа происходит обращение к нему.

    Обратите внимание на следующий пример:

    class Parent { static int a; }

    class Child extends Parent { }

    Каков будет результат следующих строк?

    Child.a=10; Parent.a=5; System.out.println(Child.a);

    В этом примере поле a не было скрыто и передалось по наследству классу Child. Однако результат показывает, что это все же одно поле:

    5

    Несмотря на то, что к полю класса идут обращения через разные классы, переменная всего одна.

    Итак, наследники могут объявлять поля с именами, совпадающими с родительскими полями. Такие объявления называют скрывающими. При этом объекты будут содержать оба значения, а компилятор будет каждый раз определять, с каким из них надо работать.

    Поля класса CardPanel

    В полях pBgColor, pFgColor и pFont хранятся ссылки на панели страниц блокнота, которые мы разместим внутри панели класса CardPanel:
    Panel pBgColor; Panel pFgColor; Panel pFont;
    Кроме того, в поле pControlled хранится ссылка на верхнюю панель с текстовой строкой First Panel.
    Panel pControlled;
    Это поле будет проинициализировано конструктором класса CardPanel.
    В следующих трех полях мы храним ссылки на списки класса Choice, предназначенные, соответственно, для выбора цвета текста, цвета фона и шрифта:
    Choice chBgColor; Choice chFgColor; Choice chFont;
    Три поля класса Label содержат ссылки на подписи к указанным выше спискам:
    Label lbBgColor; Label lbFgColor; Label lbFont;

    Поля класса ControlPanel

    Следующие пять полей хранят ссылки на кнопки, управляющие страницами блокнота:
    Button btNext; Button btPrev; Button btBgColor; Button btFgColor; Button btFont;
    Поле pCard хранит ссылку на панель блокнота:
    Panel pCard;
    Эта ссылка инициализируется конструктором класса.

    Применение интерфейсов

    До сих пор интерфейсы рассматривались с технической точки зрения – как их объявлять, какие конфликты могут возникать, как их разрешать. Однако важно понимать, как применяются интерфейсы с концептуальной точки зрения.
    Распространенное мнение, что интерфейс – это полностью абстрактный класс, в целом верно, но оно не отражает всех преимуществ, которые дают интерфейсы объектной модели. Как уже отмечалось, множественное наследование порождает ряд конфликтов, но отказ от него, хоть и делает язык проще, но не устраняет ситуации, в которых требуются подобные подходы.
    Возьмем в качестве примера дерева наследования классификацию живых организмов. Известно, что растения и животные принадлежат к разным царствам. Основным различием между ними является то, что растения поглощают неорганические элементы, а животные питаются органическими веществами. Животные делятся на две большие группы – птицы и млекопитающие. Предположим, что на основе этой классификации построено дерево наследования, в каждом классе определены элементы с учетом наследования от родительских классов.
    Рассмотрим такое свойство живого организма, как способность питаться насекомыми. Очевидно, что это свойство нельзя приписать всей группе птиц, или млекопитающих, а тем более растений. Но существуют представители каждой из названных групп, которые этим свойством обладают, – для растений это росянка, для птиц, например, ласточки, а для млекопитающих – муравьеды. Причем, очевидно, "реализовано" это свойство у каждого вида совсем по-разному.
    Можно было бы объявить соответствующий метод (скажем, consumeInsect(Insect)) у каждого представителя независимо. Но если задача состоит в моделировании, например, зоопарка, то однотипную процедуру – кормление насекомыми – пришлось бы описывать для каждого вида отдельно, что существенно осложнило бы код, причем без какой-либо пользы.
    Java предлагает другое решение. Объявляется интерфейс InsectConsumer:
    public interface InsectConsumer { void consumeInsect(Insect i); }
    Его реализуют все подходящие животные и растения:

    // росянка расширяет класс растение public class Sundew extends Plant implements InsectConsumer { public void consumeInsect(Insect i) { ... } }

    // ласточка расширяет класс птица public class Swallow extends Bird implements InsectConsumer { public void consumeInsect(Insect i) { ... } } // муравьед расширяет класс млекопитающее public class AntEater extends Mammal implements InsectConsumer { public void consumeInsect(Insect i) { ... } }

    В результате в классе, моделирующем служащего зоопарка, можно объявить соответствующий метод:

    // служащий, отвечающий за кормление, // расширяет класс служащий class FeedWorker extends Worker {

    // с помощью этого метода можно накормить // и росянку, и ласточку, и муравьеда public void feedOnInsects(InsectConsumer consumer) { ... consumer.consumeInsect(insect); ... } }

    В результате удалось свести работу с одним свойством трех разнородных классов в одно место, сделать код более универсальным. Обратите внимание, что при добавлении еще одного насекомоядного такая модель зоопарка не потребует никаких изменений, чтобы обслуживать новый вид, в отличие от первоначального громоздкого решения. Благодаря введению интерфейса удалось отделить классы, реализующие его (живые организмы) и использующие его (служащий зоопарка). После любых изменений этих классов при условии сохранения интерфейса их взаимодействие не нарушится.

    Данный пример иллюстрирует, как интерфейсы предоставляют альтернативный, более строгий и гибкий подход вместо множественного наследования.

    Работа с панелями

    Назад Вперед
    Панели, создаваемые на базе класса Panel, являются мощным средством организации диалогового интерфейса. Так как класс Panel произошел от класса Container, панель может содержать компоненты и другие панели. Для каждой панели можно определить режим размещения компонент, что позволяет создавать достаточно сложный пользовательский интерфейс.
    В окне аплета вы можете создать несколько панелей, разделяющих его на части. В свою очередь, пространство, занимаемое панелями, также может быть разделено с использованием одного из описанных выше режимов размещения (рис. 1).
    Работа с панелями
    Рис. 1. Размещение нескольких панелей в окне аплета
    Отдельные панели могут содержать в себе такие компоненты, как кнопки, переключатели, списки, текстовые поля и так далее.
    Назад Вперед

    Работа с панелямиРабота с панелями Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Работа с панелями
    printmenus();
    Работа с панелямиПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Работа с панелями»


    Работа с панелямиSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Работа с панелями»


    Работа с панелямиКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Работа с панелями»


    Работа с панелямиГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Работа с панелями»


    Работа с панелямиОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Работа с панелями»


    Работа с панелямиПроекты
    События
    Lab Downloads
    Посмотреть все Работа с панелями»


    Работа с панелями

    Реализация интерфейса

    Каждый класс может реализовывать любые доступные интерфейсы. При этом в классе должны быть реализованы все абстрактные методы, появившиеся при наследовании от интерфейсов или родительского класса, чтобы новый класс мог быть объявлен неабстрактным.
    Если из разных источников наследуются методы с одинаковой сигнатурой, то достаточно один раз описать реализацию и она будет применяться для всех этих методов. Однако если у них различное возвращаемое значение, то возникает конфликт:
    interface A { int getValue(); }
    interface B { double getValue(); }
    Если попытаться объявить класс, реализующий оба эти интерфейса, то возникнет ошибка компиляции. В классе оказывается два разных метода с одинаковой сигнатурой, что является неразрешимым конфликтом. Это единственное ограничение на набор интерфейсов, которые может реализовывать класс.
    Подобный конфликт с полями-константами не столь критичен:
    interface A { int value=3; } interface B { double value=5.4; } class C implements A, B { public static void main(String s[]) { C c = new C(); // System.out.println(c.value); - ошибка! System.out.println(((A)c).value); System.out.println(((B)c).value); } }
    Как видно из примера, обращаться к такому полю через сам класс нельзя, компилятор не сможет понять, какое из двух полей нужно использовать. Но можно с помощью явного приведения сослаться на одно из них.
    Итак, если имя интерфейса указано после implements в объявлении класса, то класс реализует этот интерфейс. Наследники данного класса также реализуют интерфейс, поскольку им достаются по наследству его элементы.
    Если интерфейс A наследуется от интерфейса B, а класс реализует A, то считается, что интерфейс B также реализуется этим классом по той же причине – все элементы передаются по наследству в два этапа – сначала интерфейсу A, затем классу.
    Наконец, если класс C1 наследуется от класса C2, класс C2 реализует интерфейс A1, а интерфейс A1 наследуется от интерфейса A2, то класс C1 также реализует интерфейс A2.
    Все это позволяет утверждать, что переменные типа интерфейс также допустимы. Они могут иметь значение null, или ссылаться на объекты, порожденные от классов, реализующих этот интерфейс. Поскольку объекты порождаются только от классов, а все они наследуются от Object, это означает, что значения типа интерфейс обладают всеми элементами класса Object.

    Рисование в окне панели

    Назад Вперед
    Как вы знаете, для того чтобы что-нибудь нарисовать, необходимо вначале получить контекст отображения. Методу paint передается контекст отображения, связанный с окном аплета. Если в окне имеются панели, то для рисования внутри них необходимо получить контекст отображения окон панелей.
    Проще всего это сделать с помощью метода getGraphics, вызвав его для объекта класса Panel:
    Graphics gpDraw; gpDraw = pDraw.getGraphics();
    Здесь в переменную gpDraw мы записали ссылку на контекст отображения для панели pDraw.
    Получив контекст отображения, можно приступить к рисованию. Вот, например, как можно нарисовать вокруг панели тонкую рамку:
    Dimension dimAppWndDimension = pDraw.size(); gpDraw.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);
    В этом фрагменте кода мы вначале определили размеры панели, вызвав для нее метод size, а затем при помощи метода drawRect, вызванного для контекста отображения gpDraw, нарисовали рамку.
    Для установки шрифта и рисования текста в окне панели вы также должны указывать ссылку на контекст отображения вашей панели:
    gpDraw.setFont(new Font("Courier", Font.PLAIN, 12)); gpDraw.drawString( "Текст внутри окна панели", 10, 50);
    Другой способ основан на создании собственного класса на базе класса Panel и переопределения в этом классе метода paint.
    Назад Вперед

    Рисование в окне панелиРисование в окне панели Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Рисование в окне панели
    printmenus();
    Рисование в окне панелиПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Рисование в окне панели»


    Рисование в окне панелиSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Рисование в окне панели»


    Рисование в окне панелиКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Рисование в окне панели»


    Рисование в окне панелиГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО

    Создание нового класса на базе класса Panel

    Назад Вперед
    Если ваш аплет создает много панелей, техника рисования в окнах этих панелей, описанная выше, может привести к усложнению исходного текста приложения. Так как рисование в окнах панелей выполняется в методе paint класса аплета, вам придется получать контекст отображения для каждой панели.
    Намного проще создать несколько дочерних классов от класса Panel, переопределив в каждом из них метод paint. В этом случае для каждой панели вы можете создать свой метода paint, которому будет автоматически передаваться контекст отображения, связанный с окном соответствующей панели.
    В аплете Options, который мы рассмотрим ниже, использована именно такая методика работы с панелями.
    Назад Вперед

    Создание нового класса на базе класса PanelСоздание нового класса на базе класса Panel Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Создание нового класса на базе класса Panel
    printmenus();
    Создание нового класса на базе класса PanelПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Создание нового класса на базе класса Panel»


    Создание нового класса на базе класса PanelSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Создание нового класса на базе класса Panel»


    Создание нового класса на базе класса PanelКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Создание нового класса на базе класса Panel»


    Создание нового класса на базе класса PanelГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Создание нового класса на базе класса Panel»


    Создание нового класса на базе класса PanelОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Создание нового класса на базе класса Panel»


    Создание нового класса на базе класса PanelПроекты
    События
    Lab Downloads
    Посмотреть все Создание нового класса на базе класса Panel»


    Создание нового класса на базе класса Panel

    Создание панелей

    Назад Вперед
    Панель создается очень просто. Прежде всего необходимо выбрать для окна аплета схему размещения компонент, соответствующую требуему расположению панелей. Например, для создания в окне аплета двух панелей, разделяющих его по горизонтали, следует выбрать режим GridLayout:
    setLayout(new GridLayout(2, 1));
    Панели будут размещаться в ячейках таблицы, состоящей из одного столбца и двух строк.
    Далее нужно создать объекты класса Panel:
    Panel pTopPanel; pTopPanel = new Panel(); Panel pBottomPanel; pBottomPanel = new Panel();
    Ссылка на панель, которая будет располагаться сверху, записывается в переменную pTopPanel, а на ту, что будет располагаться снизу - в переменную pBottomPanel.
    Назад Вперед

    Создание панелейСоздание панелей Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Создание панелей
    printmenus();
    Создание панелейПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Создание панелей»


    Создание панелейSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Создание панелей»


    Создание панелейКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Создание панелей»


    Создание панелейГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Создание панелей»


    Создание панелейОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Создание панелей»


    Создание панелейПроекты
    События
    Lab Downloads
    Посмотреть все Создание панелей»


    Создание панелей

    Статические элементы

    До этого момента под полями объекта мы всегда понимали значения, которые имеют смысл только в контексте некоторого экземпляра класса. Например:
    class Human { private String name; }
    Прежде, чем обратиться к полю name, необходимо получить ссылку на экземпляр класса Human, невозможно узнать имя вообще, оно всегда принадлежит какому-то конкретному человеку.
    Но бывают данные и иного характера. Предположим, необходимо хранить количество всех людей (экземпляров класса Human, существующих в системе). Понятно, что общее число людей не является характеристикой какого-то одного человека, оно относится ко всему типу в целом. Отсюда появляется название "поле класса", в отличие от "поля объекта". Объявляются такие поля с помощью модификатора static:
    class Human { public static int totalCount; }
    Чтобы обратиться к такому полю, ссылка на объект не требуется, вполне достаточно имени класса:
    Human.totalCount++; // рождение еще одного человека
    Для удобства разрешено обращаться к статическим полям и через ссылки:
    Human h = new Human(); h.totalCount=100;
    Однако такое обращение конвертируется компилятором. Он использует тип ссылки, в данном случае переменная h объявлена как Human, поэтому последняя строка будет неявно преобразована в:
    Human.totalCount=100;
    В этом можно убедиться на следующем примере:
    Human h = null; h.totalCount+=10;
    Значение ссылки равно null, но это не имеет значения в силу описанной конвертации. Данный код успешно скомпилируется и корректно исполнится. Таким образом, в следующем примере
    Human h1 = new Human(), h2 = new Human(); Human.totalCount=5; h1.totalCount++; System.out.println(h2.totalCount);
    все обращения к переменной totalCount приводят к одному единственному полю, и результатом работы такой программы будет 6. Это поле будет существовать в единственном экземпляре независимо от того, сколько объектов было порождено от данного класса, и был ли вообще создан хоть один объект.
    Аналогично объявляются статические методы.
    class Human { private static int totalCount;

    public static int getTotalCount() { return totalCount; } }

    Для вызова статического метода ссылки на объект не требуется.

    Human.getTotalCount();

    Хотя для удобства обращения через ссылку разрешены, но принимается во внимание только тип ссылки:

    Human h=null; h.getTotalCount(); // два эквивалентных Human.getTotalCount(); // обращения к одному // и тому же методу

    Хотя приведенный пример технически корректен, все же использование ссылки на объект для обращения к статическим полям и методам не рекомендуется, поскольку это усложняет код.

    Обращение к статическому полю является корректным независимо от того, были ли порождены объекты от этого класса и в каком количестве. Например, стартовый метод main() запускается до того, как программа создаст хотя бы один объект.

    Кроме полей и методов, статическими могут быть инициализаторы. Они также называются инициализаторами класса, в отличие от инициализаторов объекта, рассматривавшихся ранее. Их код выполняется один раз во время загрузки класса в память виртуальной машины. Их запись начинается с модификатора static:

    class Human { static { System.out.println("Class loaded"); } }

    Если объявление статического поля совмещается с его инициализацией, то поле инициализируется также однократно при загрузке класса. На объявление и применение статических полей накладываются те же ограничения, что и для динамических,– нельзя использовать поле в инициализаторах других полей или в инициализаторах класса до того, как это поле объявлено:

    class Test { static int a; static { a=5; // b=7; // Нельзя использовать до // объявления! } static int b=a; }

    Это правило распространяется только на обращения к полям по простому имени. Если использовать составное имя, то обращаться к полю можно будет раньше (выше в тексте программы), чем оно будет объявлено:

    class Test { static int b=Test.a; static int a=3; static { System.out.println("a="+a+", b="+b); } }

    Если класс будет загружен в систему, на консоли появится текст:

    a=3, b=0


    Видно, что поле b при инициализации получило значение по умолчанию поля a, т.е. 0. Затем полю a было присвоено значение 3.

    Статические поля также могут быть объявлены как final, это означает, что они должны быть проинициализированы строго один раз и затем уже больше не менять своего значения. Аналогично, статические методы могут быть объявлены как final, а это означает, что их нельзя перекрывать в классах-наследниках.

    Для инициализации статических полей можно пользоваться статическими методами и нельзя обращаться к динамическим. Вводят специальные понятия – статический и динамический контексты. К статическому контексту относят статические методы, статические инициализаторы, инициализаторы статических полей. Все остальные части кода имеют динамический контекст.

    При выполнении кода в динамическом контексте всегда есть объект, с которым идет работа в данный момент. Например, для динамического метода это объект, у которого он был вызван, и так далее.

    Напротив, со статическим контекстом ассоциированных объектов нет. Например, как уже указывалось, стартовый метод main() вызывается в тот момент, когда ни один объект еще не создан. При обращении к статическому методу, например, MyClass.staticMethod(), также может не быть ни одного экземпляра MyClass. Обращаться к статическим методам класса Math можно, а создавать его экземпляры нельзя.

    А раз нет ассоциированных объектов, то и пользоваться динамическими конструкциями нельзя. Можно только ссылаться на статические поля и вызывать статические методы. Либо обращаться к объектам через ссылки на них, полученные в результате вызова конструктора или в качестве аргумента метода и т.п.

    class Test { public void process() { } public static void main(String s[]) { // process(); - ошибка! // у какого объекта его вызывать?

    Test test = new Test(); test.process(); // так правильно } }

    В этой лекции были рассмотрены

    В этой лекции были рассмотрены особенности объектной модели Java. Это, во-первых, статические элементы, позволяющие использовать интерфейс класса без создания объектов. Нужно помнить, что, хотя для обращения к статическим элементам можно задействовать ссылочную переменную, на самом деле ее значение не используется, компилятор основывается только на ее типе.
    Для правильной работы со статическими элементами вводятся понятия статического и динамического контекста.
    Далее рассматривалось использование ключевых слов this и super. Выражение this предоставляет ссылку, указывающую на объект, в контексте которого оно встречается. Эта конструкция помогает избегать конфликтов имен, а также применяется в конструкторах.
    Слово super позволяет задействовать свойства родительского класса, что необходимо для реализации переопределенных методов, а также в конструкторах.
    Затем было введено понятие абстрактного метода и класса. Абстрактный метод не имеет тела, он лишь указывает, что метод с такой сигнатурой должен быть реализован в классе-наследнике. Поскольку он не имеет собственной реализации, классы с абстрактными методами также должны быть объявлены с модификатором abstract, который указывает, что от них нельзя порождать объекты. Основная цель абстрактных методов – описать в родительском классе как можно больше общих свойств наследников, пусть даже и в виде заголовков методов без реализации.
    Следующее важное понятие – особый тип в Java, интерфейс. Его еще называют полностью абстрактным классом, так как все его методы обязательно абстрактные, а поля final static. Соответственно, на основе интерфейсов невозможно создавать объекты.
    Интерфейсы являются альтернативой множественному наследованию. Классы не могут иметь более одного родителя, но они могут реализовывать сколько угодно интерфейсов. Таким образом, интерфейсы описывают общие свойства классов, не находящихся на одной ветви дерева наследования.
    Наконец, важным свойством объектной модели является полиморфизм. Было подробно изучено поведение полей и методов, как статических, так и динамических, при переопределении. Что позволило перейти к вопросу соответствия типов переменной и ее значения.

    Программирование на Java

    Инициализация массивов

    Теперь, когда мы выяснили, как создавать экземпляры массива, рассмотрим, какие значения принимают его элементы.
    Если создать массив на основе примитивного числового типа, то изначально после создания все элементы массива имеют значение по умолчанию, то есть 0. Если массив объявлен на основе примитивного типа boolean, то и в этом случае все элементы будут иметь значение по умолчанию false. Выше рассматривался пример инициализации элементов с помощью цикла for.
    Рассмотрим создание массива на основе ссылочного типа. Предположим, это будет класс Point. При создании экземпляра массива с применением ключевого слова new не создается ни один объект класса Point, создается лишь один объект массива. Каждый элемент массива будет иметь пустое значение null. В этом можно убедиться на простом примере:
    Point p[]=new Point[5]; for (int i=0; i Результатом будут лишь слова null.
    Далее нужно инициализировать элементы массива по отдельности, например, в цикле. Вообще, создание массива длиной n можно рассматривать как заведение n переменных и работать с элементами массива (в последнем примере p[i]) по правилам обычных переменных.
    Кроме того, существует и другой способ создания массивов – инициализаторы. В этом случае ключевое слово new не используется, а ставятся фигурные скобки, и в них через запятую перечисляются значения всех элементов массива. Например, для числового массива явная инициализация записывается следующим образом:
    int i[]={1, 3, 5}; int j[]={}; // эквивалентно new int[0]
    Длина массива вычисляется автоматически, исходя из количества введенных значений. Далее создается массив такой длины и каждому его элементу присваивается указанное значение.
    Аналогично можно порождать массивы на основе объектных типов, например:
    Point p=new Point(1,3); Point arr[]={p, new Point(2,2), null, p}; // или String sarr[]={"aaa", "bbb", "cde"+"xyz"};
    Однако инициализатор нельзя использовать для анонимного создания экземпляров массива, то есть не для инициализации переменной, а, например, для передачи параметров метода или конструктора.
    Например:

    public class Parent { private String[] values;

    protected Parent(String[] s) { values=s; } }

    public class Child extends Parent {

    public Child(String firstName, String lastName) { super(???); // требуется анонимное создание массива } }

    В конструкторе класса Child необходимо осуществить обращение к конструктору родителя и передать в качестве параметра ссылку на массив. Теоретически можно передать null, но это приведет в большинстве случаев к некорректной работе классов. Можно вставить выражение new String[2], но тогда вместо значений firstName и lastName будут переданы пустые строки. Попытка записать {firstName, lastName} приведет к ошибке компиляции, так можно только инициализировать переменные.

    Корректное выражение выглядит так:

    new String[]{firstName, lastName}

    Что является некоторой смесью выражения, создающего массивы с помощью new, и инициализатора. Длина массива определяется количеством указанных значений.

    Исходный текст приложения MenuApp

    Назад Вперед
    Исходный текст приложения MenuApp представлен в листинге 1.
    Листинг 1. Файл MenuApp.java
    import java.awt.*;
    public class MenuApp { public static void main(String args[]) { MainFrameWnd frame = new MainFrameWnd("MenuApp");
    frame.setSize( frame.getInsets().left + frame.getInsets().right + 320, frame.getInsets().top + frame.getInsets().bottom + 240);
    frame.show(); } }
    class MainFrameWnd extends Frame { MenuBar mbMainMenuBar; Menu mnFile; Menu mnHelp;
    public MainFrameWnd(String sTitle) { super(sTitle);
    setSize(400, 200);
    setBackground(Color.yellow); setForeground(Color.black);
    setLayout(new FlowLayout());
    mbMainMenuBar = new MenuBar();
    mnFile = new Menu("File");
    mnFile.add("New"); mnFile.add("-"); mnFile.add("Exit");
    mnHelp = new Menu("Help");
    mnHelp.add("Content"); mnHelp.add("-"); mnHelp.add("About");
    mbMainMenuBar.add(mnFile); mbMainMenuBar.add(mnHelp);
    setMenuBar(mbMainMenuBar); }
    public void paint(Graphics g) { g.setFont(new Font( "Helvetica", Font.PLAIN, 12));
    g.drawString("Frame window", 10, 70);
    super.paint(g); }
    public boolean handleEvent(Event evt) { if(evt.id == Event.WINDOW_DESTROY) { setVisible(false); System.exit(0); return true; } else return super.handleEvent(evt); }
    public boolean action(Event evt, Object obj) { MenuItem mnItem;
    if(evt.target instanceof MenuItem) { mnItem = (MenuItem)evt.target;
    if(obj.equals("Exit")) { System.exit(0); }
    else if(obj.equals("New")) { MessageBox mbox;
    mbox = new MessageBox( "Item New selected", this, "Dialog from Frame", true); mbox.show(); }
    else if(obj.equals("Content")) { MessageBox mbox;
    mbox = new MessageBox( "Item Content selected", this, "Dialog from Frame", true); mbox.show(); }
    else if(obj.equals("About")) { MessageBox mbox; mbox = new MessageBox( "Item About selected", this, "Dialog from Frame", true); mbox.show(); } else return false; return true; } return false; } }

    Использование класса Dialog

    Для того чтобы создать свою диалоговую панель, вы должны определить новый класс, унаследовав его от класса Dialog, как это показано ниже:
    class MessageBox extends Dialog { . . . public MessageBox(String sMsg, Frame parent, String sTitle, boolean modal) { super(parent, sTitle, modal); . . . resize(200, 100); . . . } }
    В этом классе нужно определить конструктор, который вызывает конструктор базового метода класса Dialog и определяет размеры окна диалоговой панели. Кроме того, в конструкторе вы должны создать все необходимые компоненты для размещения внутри диалоговой панели (кнопки, списки, текстовые поля, переключатели и так далее), а также выполнить размещение этих компонент, установив нужный режим размещения.
    Для окон класса Dialog устанавливается режим размещения BorderLayout. Если нужен другой режим размещения, необходимо установить его явным образом методом setLayout.
    Для отображения окна диалоговой панели необходимо вызвать метод show. Чтобы спрятать диалоговой окно, применяйте метод hide. Метод dispose удаляет окно диалоговой панели окончательно и освобождает все связанные с ним ресурсы.
    Когда пользователь пытается уничтожить окно диалоговой панели при помощи органов управления, расположенных в заголовке такого окна, возникает событие Event.WINDOW_DESTROY. Вы должны обработать его, обеспечив удаление окна диалоговой панели вызовом метода dispose, если, конечно, это соответствует логике работы вашей панели.
    Назад Вперед

    Использование класса DialogИспользование класса Dialog Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Использование класса Dialog
    printmenus();
    Использование класса DialogПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Использование класса Dialog»


    Использование класса DialogSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Использование класса Dialog»


    Использование класса DialogКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Использование класса Dialog»


    Использование класса DialogГарантийное обслуживание

    Класс MainFrameWnd

    Класс MainFrameWnd создан на базе класса Frame:
    class MainFrameWnd extends Frame { . . . }
    В нем мы определили три поля, конструктор, методы paint, handleEvent и action.

    Класс массива

    Поскольку массив является объектным типом данных, можно попытаться представить себе, как выглядело бы объявление класса такого типа. На самом деле эти объявления не хранятся в файлах, или еще каком-нибудь формате. Учитывая, что массив может быть объявлен на основе любого типа и иметь произвольную размерность, это физически невыполнимо, да и не требуется. Вместо этого во время выполнения приложения виртуальная машина генерирует эти объявления динамически на основе базового типа и размерности, а затем они хранятся в памяти в виде таких же экземпляров класса Class, как и для любых других типов.
    Рассмотрим гипотетическое объявление класса для массива, основанного на неком объектном типе Element.
    Объявление класса начинается с перечисления модификаторов, среди которых особую роль играют модификаторы доступа. Класс массива будет иметь такой же уровень доступа, как и базовый тип. То есть если Element объявлен как public-класс, то и массив будет иметь уровень доступа public. Для любого примитивного типа класс массива будет public. Можно также указать модификатор final, поскольку никакой класс не может наследоваться от класса массива.
    Затем следует имя класса, на котором можно подробно не останавливаться, т.к. к типу массив обращение идет не по его имени, а по имени базового типа и набору квадратных скобок.
    Затем нужно указать родительский класс. Все массивы наследуются напрямую от класса Object. Далее перечисляются интерфейсы, которые реализует класс. Для массива это будут интерфейсы Cloneable и Serializable. Первый из них подробно рассматривается в конце этой лекции, а второй будет описан в следующих лекциях.
    Тело класса содержит объявление одного public final поля length типа int. Кроме того, переопределен метод clone() для поддержки интерфейса Cloneable.
    Сведем все вышесказанное в формальную запись класса:
    [public] class A implements Cloneable, java.io.Serializable { public final int length; // инициализируется при создании public Object clone() { try { return super.clone();} catch (CloneNotSupportedException e) { throw new InternalError(e.getMessage()); } } }
    Таким образом, экземпляр типа массив является полноценным объектом, который, в частности, наследует все методы, определенные в классе Object, например, toString(), hashCode() и остальные.
    Например:
    // результат работы метода toString() System.out.println(new int[3]); System.out.println(new int[3][5]); System.out.println(new String[2]);
    // результат работы метода hashCode() System.out.println(new float[2].hashCode());
    Результатом выполнения программы будет:
    [I@26b249 [[I@82f0db [Ljava.lang.String;@92d342 7051261

    Класс Menu

    Назад Вперед
    Для того чтобы дать вам представление о том, что можно делать с меню, приведем краткое описание класса Menu:

    Класс MenuApp

    В главном классе приложения MenuApp мы определили только один метод main. Этот метод получает управление при запуске приложения.
    Первым делом метод main создает объект класса MainFrameWnd, определенного в нашем приложении:
    MainFrameWnd frame = new MainFrameWnd("MenuApp");
    Этот класс, созданный на базе класса Frame, определяет поведение главного окна нашего приложения.
    На втором шаге метод init настраивает размеры главного окна с учетом размеров внешней рамки и заголовка окна:
    frame.setSize(frame.getInsets().left + frame.getInsets().right + 320, frame.getInsets().top + frame.getInsets().bottom + 240);
    Поля left и right объекта класса Insets, ссылку на который возвращает метод getInsets, содержат ширину левой и правой части рамки окна, соответственно. Поле top содержит высоту верхней части рамки окна с учетом заголовка, а поле bottom - высоту нижней части рамки окна.
    Для отображения окна фрейма мы вызываем метод show, как это показано ниже:
    frame.show();

    Класс MenuItem

    Назад Вперед
    Класс MenuItem определяет поведение отдельных элементов меню.
    Пользуясь методами класса MenuItem вы можете блокировать или разблокировать отдельные строки меню. Это нужно делать, например, если в данный момент функция, соответствующая строке меню, недоступна или не определена. Вы также можете изменять текстовые строки, соответствующие элементам меню, что может пригодиться для переопределения их назначения.

    Класс MessageBox

    Для отображения названий выбранных строк меню мы создаем диалоговую панель, определив свой класс MessageBox на базе класса Dialog, как это показано ниже:
    class MessageBox extends Dialog { . . . }
    В классе MessageBox есть два поля, конструктор, методы handleEvent и action.

    Клонирование массивов

    Итак, любой массив может быть клонирован. В этом разделе хотелось бы рассмотреть особенности, возникающие из-за того, что Object.clone() копирует только один объект.
    Рассмотрим пример:
    int a[]={1, 2, 3}; int b[]=(int[])a.clone(); a[0]=0; System.out.println(b[0]);
    Результатом будет единица, что вполне очевидно, так как весь массив представлен одним объектом, который не будет зависеть от своей копии. Усложняем пример:
    int a[][]={{1, 2}, {3}}; int b[][]=(int[][]) a.clone();
    if (...) { // первый вариант: a[0]=new int[]{0}; System.out.println(b[0][0]); } else { // второй вариант: a[0][0]=0; System.out.println(b[0][0]); }
    Разберем, что будет происходить в этих двух случаях. Начнем с того, что в первой строке создается двухмерный массив, состоящий из двух одномерных. Итого три объекта. Затем, на следующей строке при клонировании будет создан новый двухмерный массив, содержащий ссылки на те же самые одномерные массивы.
    Теперь несложно предсказать результат обоих вариантов. В первом случае в исходном массиве меняется ссылка, хранящаяся в первом элементе, что не принесет никаких изменений для клонированного объекта. На консоли появится 1.
    Во втором случае модифицируется существующий массив, что скажется на обоих двухмерных массивах. На консоли появится 0.
    Обратите внимание, что если из примера убрать условие if-else, так, чтобы отрабатывал первый вариант, а затем второй, то результатом будет опять 1, поскольку в части второго варианта модифицироваться будет уже новый массив, порожденный в части первого варианта.
    Таким образом, в Java предоставляется мощный, эффективный и гибкий механизм клонирования, который легко применять и модифицировать под конкретные нужды. Особенное внимание должно уделяться копированию объектных полей, которые по умолчанию копируются только по ссылке.

    Клонирование

    Механизм клонирования, как следует из названия, позволяет порождать новые объекты на основе существующего, которые обладали бы точно таким же состоянием, что и исходный. То есть ожидается, что для исходного объекта, представленного ссылкой x, и результата клонирования, возвращаемого методом x.clone(), выражение
    x == x.clone()
    должно быть истинным, как и выражение
    x.clone().getClass() == x.getClass()
    Наконец, выражение
    x.equals(x.clone())
    также верно. Реализация такого метода clone() осложняется целым рядом потенциальных проблем, например:
  • класс, от которого порожден объект, может иметь разнообразные конструкторы, которые к тому же могут быть недоступны (например, модификатор доступа private);
  • цепочка наследования, которой принадлежит исходный класс, может быть довольно длинной, и каждый родительский класс может иметь свои поля – недоступные, но важные для воссоздания состояния исходного объекта;
  • в зависимости от логики реализации возможна ситуация, когда не все поля должны копироваться для корректного клонирования; одни могут оказаться лишними, другие потребуют дополнительных вычислений или преобразований;
  • возможна ситуация, когда объект нельзя клонировать, дабы не нарушить целостность системы.

  • Поэтому было реализовано следующее решение.
    Класс Object содержит метод clone(). Рассмотрим его объявление:
    protected native Object clone() throws CloneNotSupportedException;
    Именно он используется для клонирования. Далее возможны два варианта.
    Первый вариант: разработчик может в своем классе переопределить этот метод и реализовать его по своему усмотрению, решая перечисленные проблемы так, как того требует логика разрабатываемой системы. Упомянутые условия, которые должны быть истинными для клонированного объекта, не являются обязательными и программист может им не следовать, если это требуется для его класса.
    Второй вариант предполагает использование реализации метода clone() в самом классе Object. То, что он объявлен как native, говорит о том, что его реализация предоставляется виртуальной машиной. Естественно, перечисленные трудности легко могут быть преодолены самой JVM, ведь она хранит в памяти все свойства объектов.
    При выполнении метода clone() сначала проверяется, можно ли клонировать исходный объект. Если разработчик хочет сделать объекты своего класса доступными для клонирования через Object.clone(), то он должен реализовать в своем классе интерфейс Cloneable. В этом интерфейсе нет ни одного элемента, он служит лишь признаком для виртуальной машины, что объекты могут быть клонированы. Если проверка не выполняется успешно, метод порождает ошибку CloneNotSupportedException.
    Если интерфейс Cloneable реализован, то порождается новый объект от того же класса, от которого был создан исходный объект. При этом копирование выполняется на уровне виртуальной машины, никакие конструкторы не вызываются. Затем значения всех полей, объявленных, унаследованных либо объявленных в родительских классах, копируются. Полученный объект возвращается в качестве клона.
    Обратите внимание, что сам класс Object не реализует интерфейс Cloneable, а потому попытка вызова new Object().clone() будет приводить к ошибке времени исполнения. Метод clone() предназначен скорее для использования в наследниках, которые могут обращаться к нему с помощью выражения super.clone(). При этом могут быть сделаны следующие изменения:
  • модификатор доступа расширен до public;
  • удалено предупреждение об ошибке CloneNotSupportedException;
  • результирующий объект может быть модифицирован любым способом, на усмотрение разработчика.


  • Напомним, что все массивы реализуют интерфейс Cloneable и, таким образом, доступны для клонирования.

    Важно помнить, что все поля клонированного объекта приравниваются, их значения никогда не клонируются. Рассмотрим пример:

    public class Test implements Cloneable { Point p; int height;

    public Test(int x, int y, int z) { p=new Point(x, y); height=z; }

    public static void main(String s[]) { Test t1=new Test(1, 2, 3), t2=null; try { t2=(Test) t1.clone(); } catch (CloneNotSupportedException e) {} t1.p.x=-1; t1.height=-1; System.out.println("t2.p.x=" + t2.p.x + ", t2.height=" + t2.height); } }

    Результатом работы программы будет:

    t2.p.x=-1, t2.height=3

    Из примера видно, что примитивное поле было скопировано и далее существует независимо в исходном и клонированном объектах. Изменение одного не сказывается на другом.

    А вот ссылочное поле было скопировано по ссылке, оба объекта ссылаются на один и тот же экземпляр класса Point. Поэтому изменения, происходящие с исходным объектом, сказываются на клонированном.

    Этого можно избежать, если переопределить метод clone() в классе Test.

    public Object clone() { Test clone=null; try { clone=(Test) super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(e.getMessage()); } clone.p=(Point)clone.p.clone(); return clone; }

    Обратите внимание, что результат метода Object.clone() приходится явно приводить к типу Test, хотя его реализация гарантирует, что клонированный объект будет порожден именно от этого класса. Однако тип возвращаемого значения в данном методе для универсальности объявлен как Object, поэтому явное сужение необходимо.

    Теперь метод main можно упростить:

    public static void main(String s[]) { Test t1=new Test(1, 2, 3); Test t2=(Test) t1.clone(); t1.p.x=-1; t1.height=-1; System.out.println("t2.p.x=" + t2.p.x + ", t2.height=" + t2.height); }

    Результатом будет:

    t2.p.x=1, t2.height=3

    То есть теперь все поля исходного и клонированного объектов стали независимыми.

    Реализация такого "неглубокого" клонирования в методе Object.clone() необходима, так как в противном случае клонирование второстепенного объекта могло бы привести к огромным затратам ресурсов, ведь этот объект может содержать ссылки на более значимые объекты, а те при клонировании также начали бы копировать свои поля, и так далее. Кроме того, типом поля клонируемого объекта может быть класс, не реализующий Cloneable, что приводило бы к дополнительным проблемам. Как показано в примере, при необходимости дополнительное копирование можно добавить самостоятельно.

    Конструктор класса MainFrameWnd

    В качестве единственного параметра конструктору класса MainFrameWnd передается заголовок создаваемого окна. В первой исполняемой строке наш конструктор вызывает конструктор из базового класса, передавая ему строку заголовка через параметр:
    public MainFrameWnd(String sTitle) { super(sTitle); . . . }
    Далее конструктор определяет размеры окна, вызывая для него метод setSize:
    setSize(400, 200);
    Затем мы устанавливаем для нашего окна желтый цвет фона и черный цвет изображения:
    setBackground(Color.yellow); setForeground(Color.black);
    По умолчанию для окон класса Frame устанавливается режим добавления компонент BorderLayout. Мы изменяем этот режим на FlowLayout, вызывая метод setLayout:
    setLayout(new FlowLayout());
    Далее конструктор приступает к формированию главного меню окна. Это меню создается как объект класса MenuBar:
    mbMainMenuBar = new MenuBar();
    Затем мы создаем и наполняем меню "File":
    mnFile = new Menu("File");
    mnFile.add("New"); mnFile.add("-"); mnFile.add("Exit");
    Это меню создается на базе класса Menu. Обратите внимание, что между строками New и File расположен разделитель.
    Аналогичным образом мы добавляем в главное меню другое меню - "Help":
    mnHelp = new Menu("Help");
    mnHelp.add("Content"); mnHelp.add("-"); mnHelp.add("About");
    После своего окончательного формирования меню "File" и "Help" добавляются в главное меню окна mbMainMenuBar:
    mbMainMenuBar.add(mnFile); mbMainMenuBar.add(mnHelp);
    И, наконец, когда главное меню будет сформировано, оно подключается к окну вызовом метода setMenuBar, как это показано ниже:
    setMenuBar(mbMainMenuBar);

    Конструктор класса MessageBox

    Наш конструктор создает диалоговую панель с заданным сообщением внутри нее. Ссылка на строку сообщения передается конструктору через первый параметр. Остальные параметры используются конструктором базового класса Dialog для создания диалоговой панели:
    super(parent, sTitle, modal);
    После вызова конструктора из базового класса наш конструктор устанавливает размеры окна созданной диалоговой панели, вызывая метод resize:
    resize(200, 100);
    Отменяя установленный по умолчанию режим размещения компонент BorderLayout, конструктор устанавливает режим GridLayout:
    setLayout(new GridLayout(2, 1));
    Окно диалоговой панели при этом разделяется на две части по горизонтали. В верхнюю часть добавляется текстовое поле для отображения сообщения, в нижнюю - кнопка OK:
    lbMsg = new Label(sMsg, Label.CENTER); add(lbMsg); btnOK = new Button("OK"); add(btnOK);

    Конструкторы

    Для класса Frame определено два конструктора:
    Создание окна без заголовка
    public Frame();
    Создание окна с заголовоком
    public Frame(String title);

    Создание меню с заданным названием
    public Menu(String label);
    Создание меню с заданным названием,которое может оставаться на экране после того как пользователь отпустил клавишу мыши
    public Menu(String label, boolean tearOff);


    Создание диалоговой панели без заголовка
    public Dialog(Frame parent, boolean modal);
    Создание диалоговой панели с заголовком
    public Dialog(Frame parent, String title, boolean modal);

    Массивы как тип данных в Java

    В отличие от обычных переменных, которые хранят только одно значение, массивы (arrays) используются для хранения целого набора значений. Количество значений в массиве называется его длиной, сами значения – элементами массива. Значений может не быть вовсе, в этом случае массив считается пустым, а его длина равной нулю.
    Элементы не имеют имен, доступ к ним осуществляется по номеру индекса. Если массив имеет длину n, отличную от нуля, то корректными значениями индекса являются числа от 0 до n-1. Все значения имеют одинаковый тип и говорится, что массив основан на этом базовом типе. Массивы могут быть основаны как на примитивных типах (например, для хранения числовых значений 100 измерений), так и на ссылочных (например, если нужно хранить описание 100 автомобилей в гараже в виде экземпляров класса Car).
    Сразу оговоримся, что в Java массив символов char[] и класс String являются различными типами. Их значения могут легко конвертироваться друг в друга с помощью специальных методов, но все же они не относятся к идентичным типам.
    Как уже говорилось, массивы в Java являются объектами (примитивных типов в Java всего восемь и их количество не меняется), их тип напрямую наследуется от класса Object, поэтому все элементы данного класса доступны у объектов-массивов.
    Базовый тип также может быть массивом. Таким образом конструируется массив массивов, или многомерный массив.
    Работа с любым массивом включает обычные операции, уже описанные для других типов, - объявление, инициализация и т.д. Начнем последовательно изучать их в приложении к массивам.

    Меню в окне класса Frame

    Назад Вперед
    Как мы уже говорили, окно класса Frame может иметь главное меню (Menu Bar) или, как еще говорят, строку меню. Главное меню создается на базе класса MenuBar, краткое описание которого приведено ниже.


    Этот метод обрабатывает события, возникающие при выборе строка из меню.
    В начале своей работы метод action проверяет, действительно ли событие вызвано меню:
    MenuItem mnItem;
    if(evt.target instanceof MenuItem) { . . . } return false;
    Если это так, в поле mnItem сохраняется ссылка на элемент меню, вызвавший событие:
    mnItem = (MenuItem)evt.target;
    Тем не менее, для определения строки, выбранной пользователем, нам достаточно проанализировать второй параметр метода action:
    if(obj.equals("Exit")) { System.exit(0); }
    else if(obj.equals("New")) { MessageBox mbox;
    mbox = new MessageBox( "Item New selected", this, "Dialog from Frame", true); mbox.show(); }
    else if(obj.equals("Content")) { . . . }
    else if(obj.equals("About")) { . . . }
    В данном случае второй параметр метода action будет представлять собой ссылку на строку, выбранную из меню, поэтому для определения выбранной строки мы можем выполнить простое сравнение методом equals.
    Если пользователь выбрал из меню File строку Exit, мы вызываем метод System.exit, предназначенный для завершения работы виртуальной машины Java.
    В том случае когда пользователь выбирает любую другую строку из меню, метод action создает диалоговую панель на базе определенного нами класса MessageBox. В этой диалоговой панели отображаетя название выбранной строки меню.
    Заметим, что сразу после создания конструктором диалоговая панель не появляется на экране. Мы отображаем ее, вызывая метод show.

    Метод action класса MessageBox

    Если пользователь нажимает кнопку OK, расположенную в окне диалоговой панели, метод action вызывает для панели метод dispose, удаляя эту панель с экрана и из памяти:
    if(evt.target.equals(btnOK)) { dispose(); }
    Назад Вперед

    Метод action класса MessageBoxМетод action класса MessageBox Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Метод action класса MessageBox
    printmenus();
    Метод action класса MessageBoxПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Метод action класса MessageBox»


    Метод action класса MessageBoxSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Метод action класса MessageBox»


    Метод action класса MessageBoxКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Метод action класса MessageBox»


    Метод action класса MessageBoxГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Метод action класса MessageBox»


    Метод action класса MessageBoxОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Метод action класса MessageBox»


    Метод action класса MessageBoxПроекты
    События
    Lab Downloads
    Посмотреть все Метод action класса MessageBox»


    Метод action класса MessageBox

    Метод handleEvent класса MessageBox

    Когда пользователь пытается закрыть окно диалоговой панели, например, сделав двойной щелчок левой клавишей мыши по системному меню или одиночный щелчок по кнопке удаления окна, возникает событие Event.WINDOW_DESTROY. Мы его обрабатываем следующим образом:
    if(evt.id == Event.WINDOW_DESTROY) { dispose(); return true; } else return super.handleEvent(evt);
    Вызывая метод dispose, мы удаляем окно диалоговой панели и освобождаем все связанные с ним ресурсы.

    Метод handleEvent

    Для того чтобы определить реакцию окна на попытку пользователя закрыть окно с помощью органов управления, расположенных в заголовке окна, или другим способом, мы переопределили метод handleEvent.
    При получении кода события Event.WINDOW_DESTROY (удаление окна) мы скрываем окно, вызывая метод setVisible с параметром false.
    Затем с помощью статического метода exit класса System мы завершаем работу интерпретатора:
    public boolean handleEvent(Event evt) { if(evt.id == Event.WINDOW_DESTROY) { setVisible(false); System.exit(0); return true; } else return super.handleEvent(evt); }

    Метод paint

    Метод paint получает в качестве параметра ссылку на контекст отображения, пригодный для рисования в нашем окне. Пользуясь этим контекстом, мы устанавливаем шрифт текста и рисуем текстовую строку. Затем мы вызываем метод paint из базового класса Frame, на основе которого создан наш класс MainFrameWnd:
    public void paint(Graphics g) { g.setFont(new Font( "Helvetica", Font.PLAIN, 12));
    g.drawString("Frame window", 10, 70);
    super.paint(g); }

    Методы

  • addNotify

  • Вызов метода createFrame
    public void addNotify();
    dispose
    Удаление окна и освобождение связанных с ним ресурсов
    public void dispose();
    getCursorType
    Определение типа курсора
    public int getCursorType();
  • getIconImage

  • Получение пиктограммы, установленной для окна
    public Image getIconImage();
  • getMenuBar

  • Получение ссылки на главное меню
    public MenuBar getMenuBar();
  • getTitle

  • Получение заголовка окна
    public String getTitle();
  • isResizable

  • Определение возможности изменения размеров окна пользователем
    public boolean isResizable();
  • paramString

  • Получение строки параметров
    protected String paramString();
  • remove

  • Удаление компоненты меню
    public void remove(MenuComponent m);
  • setCursor

  • Установка типа курсора
    public void setCursor(int cursorType);
  • setIconImage

  • Установка пиктограммы
    public void setIconImage(Image image);
  • setMenuBar

  • Установка главного меню
    public void setMenuBar(MenuBar mb);
  • setResizable

  • Включение или выключение возомжности изменения размеров окна
    public void setResizable(boolean resizable);
  • setTitle

  • Установка заголовка окна
    public void setTitle(String title);

  • add

  • Добавление меню в главное меню окна
    public Menu add(Menu m);
  • addNotify

  • Вызов метода createMenuBar
    public void addNotify();
  • countMenus

  • Определение количества меню, добавленных в главное меню
    public int countMenus();
  • getHelpMenu

  • Получение ссылки на меню Help
    public Menu getHelpMenu();
  • getMenu

  • Получение ссылки на меню с заданным номером
    public Menu getMenu(int i);
  • remove

  • Удаление меню с заданным номером из главного меню
    public void remove(int index);
    Удаление компоненты меню
    public void remove(MenuComponent m);
  • removeNotify

  • Извещение об удалении меню
    public void removeNotify();
  • setHelpMenu

  • Установка меню Help
    public void setHelpMenu(Menu m);


  • add

  • Добавление элемента меню
    public MenuItem add(MenuItem mi);
    Добавление строки в меню
    public void add(String label);
  • addNotify

  • Вызов метода createMenu
    public void addNotify();
  • addSeparator

  • Добавление разделителя в меню
    public void addSeparator();
  • countItems

  • Определение количества строк в меню
    public int countItems();
  • getItem

  • Получение ссылки на элемент меню с заданным номером
    public MenuItem getItem(int index);
  • isTearOff

  • Проверка, остается ли меню на экране после того как пользователь отпустил клавишу мыши
    public boolean isTearOff();
  • remove

  • Удаление заданного элемента меню
    public void remove(int index);
    Удаление заданной компоненты меню
    public void remove(MenuComponent item);
  • removeNotify

  • Извещение об удалении меню
    public void removeNotify();


  • addNotify

  • Вызов метода createMenuItem
    public void addNotify();
  • disable

  • Блокирование элемента меню
    public void disable();
  • enable

  • Разблокирование элемента меню
    public void enable();
    Блокирование или разблокирование элемента меню
    public void enable(boolean cond);
  • getLabel

  • Получение текстовой строки меню
    public String getLabel();
  • isEnabled

  • Проверка, является ли элемент меню заблокированным
    public boolean isEnabled();
  • paramString

  • Получение строки параметров
    public String paramString();
  • setLabel

  • Установка текстовой строки для элемента меню
    public void setLabel(String label);
    Назад Вперед

    МетодыМетоды Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Методы
    printmenus();
    МетодыПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Методы»


    МетодыSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Методы»


    МетодыКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Методы»


    МетодыГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Методы»


    МетодыОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Методы»


    МетодыПроекты
    События
    Lab Downloads
    Посмотреть все Методы»


    Методы


  • addNotify

  • Вызов метода createDialog
    public void addNotify();
  • getTitle

  • Получение строки заголовка диалоговой панели
    public String getTitle();
  • isModal

  • Определение, является ли диалоговая панель модальной
    public boolean isModal();
  • isResizable

  • Определение возможности изменения размеров окна диалоговой панели
    public boolean isResizable();
  • paramString

  • Получение строки параметров
    protected String paramString();
    setResizable
    Включение или выключение возможности изменения размеров окна диалоговой панели
    public void setResizable(boolean resizable);
  • setTitle

  • Установка заголовка диалоговой панели
    public void setTitle(String title);

    Многомерные массивы

    Теперь перейдем к рассмотрению многомерных массивов. Так, в следующем примере
    int i[][]=new int[3][5];
    переменная i ссылается на двумерный массив, который можно представить себе в виде таблицы 3х5. Суммарно в таком массиве содержится 15 элементов, к которым можно обращаться через комбинацию индексов от (0, 0) до (2, 4). Пример заполнения двумерного массива через цикл:
    int pithagor_table[][]=new int[5][5]; for (int i=0; i<5; i++) { for (int j=0; j<5; j++) { pithagor_table[i][j]=i*j; System.out.print(pithagor_table[i][j] + "\t"); } System.out.println(); }
    Результатом выполнения программы будет:
    0 0 0 0 0 0 1 2 3 4 0 2 4 6 8 0 3 6 9 12 0 4 8 12 16
    Однако такой взгляд на двумерные и многомерные массивы является неполным. Более точный подход заключается в том, что в Java нет двумерных, и вообще многомерных массивов, а есть массивы, базовыми типами которых являются также массивы. Например, тип int[] означает "массив чисел", а int[][] означает "массив массивов чисел". Поясним такую точку зрения.
    Если создать двумерный массив и определить переменную x, которая на него ссылается, то, используя x и два числа в паре квадратных скобок каждое (например, x[0][0]), можно обратиться к любому элементу двумерного массива. Но в то же время, используя x и одно число в паре квадратных скобок, можно обратиться к одномерному массиву, который является элементом двумерного массива. Его можно проинициализировать новым массивом с некоторой другой длиной и таблица перестанет быть прямоугольной – она примет произвольную форму. В частности, можно одному из одномерных массивов присвоить даже значение null.
    int x[][]=new int[3][5]; // прямоугольная таблица x[0]=new int[7]; x[1]=new int[0]; x[2]=null;
    После таких операций массив, на который ссылается переменная x, назвать прямоугольным никак нельзя. Зато хорошо видно, что это просто набор одномерных массивов или значений null.
    Полезно подсчитать, сколько объектов порождается выражением new int[3][5]. Правильный подсчет таков: создается один массив массивов (один объект) и три массива чисел, каждый длиной 5 (три объекта). Итого, четыре объекта.
    В рассмотренном примере три из них (массивы чисел) были тут же переопределены новыми значениями. Для таких случаев полезно использовать упрощенную форму выражения создания массивов:

    int x[][]=new int[3][];

    Такая запись порождает один объект – массив массивов – и заполняет его значениями null. Теперь понятно, что и в этом, и в предыдущем варианте выражение x.length возвращает значение 3 – длину массива массивов. Далее можно с помощью выражений x[i].length узнать длину каждого вложенного массива чисел, при условии, что i неотрицательно и меньше x.length, а также x[i] не равно null. Иначе будут возникать ошибки во время выполнения программы.

    Вообще, при создании многомерных массивов с помощью new необходимо указывать все пары квадратных скобок, соответственно количеству измерений. Но заполненной обязательно должна быть лишь крайняя левая пара, это значение задаст длину верхнего массива массивов. Если заполнить следующую пару, то этот массив заполнится не значениями по умолчанию null, а новыми созданными массивами с меньшей на единицу размерностью. Если заполнена вторая пара скобок, то можно заполнить третью, и так далее.

    Аналогично, для создания многомерных массивов можно использовать инициализаторы. В этом случае применяется столько вложенных фигурных скобок, сколько требуется:

    int i[][] = {{1,2}, null, {3}, {}};

    В этом примере порождается четыре объекта. Это, во-первых, массив массивов длиной 4, а во-вторых, три массива чисел с длинами 2, 1, 0, соответственно.

    Все рассмотренные примеры и утверждения одинаково верны для многомерных массивов, основанных как на примитивных, так и на ссылочных типах.

    Объявление массивов

    В качестве примера рассмотрим объявление переменной типа "массив, основанный на примитивном типе int":
    int a[];
    Как мы видим, сначала указывается базовый тип. Затем идет имя переменной, а пара квадратных скобок указывает на то, что используемый тип является именно массивом. Также допустима запись:
    int[] a;
    Количество пар квадратных скобок указывает на размерность массива. Для многомерных массивов допускается смешанная запись:
    int[] a[];
    Переменная a имеет тип "двумерный массив, основанный на int". Аналогично объявляются массивы с базовым объектным типом:
    Point p, p1[], p2[][];
    Создание переменной типа массив еще не создает экземпляры этого массива. Такие переменные имеют объектный тип и хранят ссылки на объекты, однако изначально имеют значение null (если они являются полями класса; напомним, что локальные переменные необходимо явно инициализировать). Чтобы создать экземпляр массива, нужно воспользоваться ключевым словом new, после чего указывается тип массива и в квадратных скобках – длина массива.
    int a[]=new int[5]; Point[] p = new Point[10];
    Переменная инициализируется ссылкой, указывающей на только что созданный массив. После его создания можно обращаться к элементам, используя ссылку на массив, далее в квадратных скобках указывается индекс элемента. Индекс меняется от нуля, пробегая всю длину массива, до максимально допустимого значения, на единицу меньшего длины массива.
    int array[]=new int[5]; for (int i=0; i<5; i++) { array[i]=i*i; } for (int j=0; j<5; j++) { System.out.println(j+"*"+j+"="+array[j]); }
    Результатом выполнения программы будет:
    0*0=0 1*1=1 2*2=4 3*3=9 4*4=16
    И далее появится ошибка времени исполнения, так как индекс превысит максимально возможное для такого массива значение. Проверка, не выходит ли индекс за допустимые пределы, происходит только во время исполнения программы, т.е. компилятор не пытается выявить эту ошибку даже в таких явных случаях, как:
    int i[]=new int[5]; i[-2]=0; // ошибка! индекс не может // быть отрицательным

    Ошибка возникнет только на этапе выполнения программы.

    Хотя при создании массива необходимо указывать его длину, это значение не входит в определение типа массива, важна лишь размерность. Таким образом, одна переменная может ссылаться на массивы разной длины:

    int i[]=new int[5]; ... i=new int[7]; // переменная та же, длина // массива другая

    Однако для объекта массива длина обязательно должна указываться при создании и уже никак не может быть изменена. В последнем примере для присвоения переменной ссылки на массив большей длины потребовалось создать новый экземпляр.

    Поскольку для экземпляра массива длина является постоянной характеристикой, для всех массивов существует специальное поле length, позволяющее узнать ее значение. Например:

    Point p[]=new Point[5]; for (int i=0; i
    Значение индекса массива всегда имеет тип int. При обращении к элементу можно также использовать byte, short или char, поскольку эти типы автоматически расширяются до int. Попытка задействовать long приведет к ошибке компиляции.

    Соответственно, и поле length имеет тип int, а теоретическая максимально возможная длина массива равняется 231-1, то есть немногим больше 2 млрд.

    Продолжая рассматривать тип массива, подчеркнем, что в качестве базового типа может использоваться любой тип Java, в том числе:

  • интерфейсы. В таком случае элементы массива могут иметь значение null или ссылаться на объекты любого класса, реализующего этот интерфейс;
  • абстрактные классы. В этом случае элементы массива могут иметь значение null или ссылаться на объекты любого неабстрактного класса-наследника.


  • Поскольку массив является объектным типом данных, его значения могут быть приведены к типу Object или, что то же самое, присвоены переменной типа Object. Например,

    Object o = new int[4];

    Это дает интересную возможность для массивов, основанных на типе Object, хранить в качестве элемента ссылку на самого себя:

    Object arr[] = new Object[3]; arr[0]=new Object(); arr[1]=null; arr[2]=arr; // Элемент ссылается // на весь массив!

    Окна и диалоговые панели

    Назад Вперед
    До сих пор мы рисовали только в окне аплета или в окнах панелей, расположенных внутри окна аплета. Однако есть и другая возможность - приложения Java, полноценные и аплеты, могут создавать обычные перекрывающиеся окна, такие, например, как окно браузера. Эти окна могут иметь меню (в отличие от окон аплетов). Пользователь может изменять размер таких окон при помощи мыши, перемещая рамку окна.
    В составе библиотеки классов AWT имеется несколько классов, предназначенных для работы с окнами. Это класс Window, который произошел от класса Container, и его дочерние классы - Frame, Dialog и FileDialog (рис. 1).
    Окна и диалоговые панели
    Рис. 1. Иерархия классов, предназначенных для создания окон
    Окно, созданное на базе класса Frame, больше всего похоже на главное окно обычного приложения Windows. Оно может иметь главное меню, для него можно устанавливать форму курсора. Внутри такого окна можно рисовать. Так как окно класса Frame (так же как и другие окна AWT) произошли от класса Container, вы можете добавлять в них различные компоненты и панели, как мы это делали с окнами аплетов и панелей.
    На базе класса Dialog создаются окна диалоговых панелей, очень похожих на обычные диалоговые панели Windows. Такие панели не могут иметь меню и обычно предназначены для запроса какой-либо информации у пользователя.
    Класс FileDialog предназначен для создания диалоговых панелей диалоговые панели, с помощью которых можно выбирать файлы на локальных дисках компьютера.
    Что же касается класса Window, то непосредственно этот класс редко применяется для создания окон, так как классы Frame, Dialog и FileDialog более удобны и обеспечивают все необходимые возможности.
    Назад Вперед

    Окна и диалоговые панелиОкна и диалоговые панели Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Окна и диалоговые панели
    printmenus();
    Окна и диалоговые панелиПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Окна и диалоговые панели»


    Окна и диалоговые панелиSolaris 10
    Java 2 Standard Edition

    Developer Tools

    Top Downloads

    New Downloads

    Патчи и обновления

    Посмотреть все Окна и диалоговые панели»

    Окна и диалоговые панелиКаталог решений

    Истории успеха

    The Sun Grid

    Партнерские программы

    Посмотреть все Окна и диалоговые панели»

    Окна и диалоговые панелиГарантийное обслуживание

    Программы SunSpectrum

    Консалтинг

    Услуги инсталляции

    Поддержка ПО

    Посмотреть все Окна и диалоговые панели»

    Окна и диалоговые панелиОписание курсов

    Сертификация

    Авторизованные учебные центры

    Посмотреть все Окна и диалоговые панели»

    Окна и диалоговые панелиПроекты

    События

    Lab Downloads

    Посмотреть все Окна и диалоговые панели»

    Окна и диалоговые панели

    Окна класса Frame

    Назад Вперед
    Ниже мы привели краткое описание класса Frame. Так как этот класс реализует интерфейс java.awt.MenuContainer, окно класса Frame может содержать меню.

    Описание исходного текста приложения MenuApp

    Назад Вперед
    Как мы уже говорили, приложение MenuApp работает автономно. Поэтому мы импортируем только класс java.awt.*, необходимый для работы с окнами:
    import java.awt.*;
    В нашем приложении определено три класса - MenuApp, MainFrameWnd и MessageBox.

    Ошибка ArrayStoreException

    Преобразование между типами массивов, основанных на ссылочных типах, может стать причиной одной довольно неочевидной ошибки.
    Рассмотрим пример:
    Child c[] = new Child[5]; Parent p[]=c; p[0]=new Parent();
    С точки зрения компилятора код совершенно корректен. Преобразование во второй строке допустимо. В третьей строке элементу массива типа Parent присваивается значение того же типа.
    Однако при выполнении такой программы возникнет ошибка. Нельзя забывать, что преобразование не меняет объект, изменяется лишь способ доступа к нему. В свою очередь, объект всегда "помнит", от какого типа он был порожден. С учетом этих замечаний становится ясно, что в третьей строке делается попытка добавить в массив Child значение типа Parent, что некорректно.
    Действительно, ведь переменная с продолжает ссылаться на этот массив, а значит, следующей строкой может быть такое обращение:
    c[0].onlyChildMethod();
    где метод onlyChildMethod() определен только в классе Child. Данное обращение совершенно корректно, а значит, недопустима ситуация, когда элемент c[0] ссылается на объект, несовместимый с Child.
    Таким образом, несмотря на отсутствие ошибок компиляции, виртуальная машина при выполнении программы всегда осуществляет дополнительную проверку перед присвоением значения элементу массива. Необходимо удостовериться, что реальный массив, существующий на момент исполнения, действительно может хранить присваиваемое значение. Если это условие нарушается, то возникает ошибка, которая называется ArrayStoreException.
    Может сложиться впечатление, что разобранная ситуация является надуманной,– зачем преобразовывать массив и тут же задавать для него неверное значение? Однако преобразование при присвоении значений является лишь примером. Рассмотрим объявление метода:
    public void process(Parent[] p) { if (p!=null && p.length>0) { p[0]=new Parent(); } }
    Метод выглядит абсолютно корректным, все потенциально ошибочные ситуации проверяются if-выражением. Однако следующий вызов этого метода все равно приводит к ошибке:
    process(new Child[3]));
    И это будет как раз ошибка ArrayStoreException.

    Переменные типа массив и их значения

    Завершим описание взаимосвязи типа переменной и типа значений, которые она может хранить.
    Как обычно, массивы, основанные на простых и ссылочных типах, мы описываем раздельно.
    Переменная типа массив примитивных величин может хранить значения только точно такого же типа, либо null.
    Переменная типа "массив ссылочных величин" может хранить следующие значения:

  • null;
  • значения точно такого же типа, что и тип переменной;
  • все значения типа массив, основанный на типе, приводимом к базовому типу исходного массива.

  • Все эти утверждения непосредственно следуют из рассмотренных выше особенностей приведения типов массивов.
    Еще раз напомним про исключительный класс Object. Переменные такого типа могут ссылаться на любые объекты, порожденные как от классов, так и от массивов.
    Сведем все эти утверждения в таблицу.

    Таблица Табл. 9.1.. Тип переменной и тип ее значения.Тип переменнойДопустимые типы ее значения
    Массив простых чисел
  • null
  • в точности совпадающий с типом переменной

  • Массив ссылочных значений
  • null
  • совпадающий с типом переменной
  • массивы ссылочных значений, удовлетворяющих следующему условию: если тип переменной – массив на основе типа A, то значение типа массив на основе типа B допустимо тогда и только тогда, когда B приводимо к A

  • Object
  • null
  • любой ссылочный, включая массивы



  • Поля

    С помощью полей класса Frame вы можете задавать для своего окна различные типы курсоров:
    public final static int CROSSHAIR_CURSOR; public final static int DEFAULT_CURSOR; public final static int E_RESIZE_CURSOR; public final static int HAND_CURSOR; public final static int MOVE_CURSOR; public final static int N_RESIZE_CURSOR; public final static int NE_RESIZE_CURSOR; public final static int NW_RESIZE_CURSOR; public final static int S_RESIZE_CURSOR; public final static int SE_RESIZE_CURSOR; public final static int SW_RESIZE_CURSOR; public final static int TEXT_CURSOR; public final static int W_RESIZE_CURSOR; public final static int WAIT_CURSOR;

    Поля класса MainFrameWnd

    Поле mbMainMenuBar предназанчено для хранения ссылки на главное меню приложения, создаваемое как объект класса MenuBar:
    MenuBar mbMainMenuBar;
    Поля mnFile и mnHelp хранят ссылки на меню File и Help, соответственно:
    Menu mnFile; Menu mnHelp;
    Данные меню создаются на базе класса Menu.

    Поля класса MessageBox

    Внутри диалоговой панели мы расположили текстовое поле класса Label, предназначенное для отображения сообщения, и кнопку с надписью OK, с помощью которой можно завершить работу диалоговой панели.
    Ссылка на текстовое поле хранится в поле lbMsg, на кнопку - в поле btnOK.

    Преобразование типов для массивов

    Теперь, когда массив введен как полноценный тип данных в Java, рассмотрим, какое влияние он окажет на преобразование типов.
    Ранее подробно рассматривались переходы между примитивными и обычными (не являющимися массивами) ссылочными типами. Хотя массивы являются объектными типами, их также будет полезно разделить по базовому типу на две группы – основанные на примитивном или ссылочном типе.
    Имейте в виду, что переходы между массивами и примитивными типами являются запрещенными. Преобразования между массивами и другими объектными типами возможны только для класса Object и интерфейсов Cloneable и Serializable. Массив всегда можно привести к этим трем типам, обратный же переход является сужением и должен производиться явным образом по усмотрению разработчика. Таким образом, интерес представляют только переходы между разными типами массивов. Очевидно, что массив, основанный на примитивном типе, принципиально нельзя преобразовать к типу массива, основанному на ссылочном типе, и наоборот.
    Пока не будем останавливаться на этом подробно, однако заметим, что преобразования между типами массивов, основанных на различных примитивных типах, невозможны ни при каких условиях.
    Для ссылочных же типов такого строгого правила нет. Например, если создать экземпляр массива, основанного на типе Child, то ссылку на него можно привести к типу массива, основанного на типе Parent.
    Child c[] = new Child[3]; Parent p[] = c;
    Вообще, существует универсальное правило: массив, основанный на типе A, можно привести к массиву, основанному на типе B, если сам тип A приводится к типу B.
    // если допустимо такое приведение: B b = (B) new A(); // то допустимо и приведение массивов: B b[]=(B[]) new A[3];
    Применяя это правило рекурсивно, можно преобразовывать многомерные массивы. Например, массив Child[][] можно привести к Parent[][], так как их базовые типы приводимы (Child[] к Parent[]) также на основе этого правила (поскольку базовые типы Child и Parent приводимы в силу правил наследования).
    Как обычно, расширения можно проводить неявно (как в предыдущем примере), а сужения – только явным приведением.
    Вернемся к массивам, основанным на примитивном типе. Невозможность их участия в преобразованиях типов связана, конечно, с различиями между простыми и ссылочными типами данных. Поскольку элементами объектных массивов являются ссылки, они легко могут участвовать в приведении. Напротив, элементы простых типов действительно хранят числовые или булевские значения. Предположим, такое преобразование осуществимо:
    // пример вызовет ошибку компиляции byte b[]={1, 2, 3}; int i[]=b;
    В таком случае, элементы b[0] и i[0] хранили бы значения разных типов. Стало быть, преобразование потребовало бы копирования с одновременным преобразованием типа всех элементов исходного массива. В результате был бы создан новый массив, элементы которого равнялись бы по значению элементам исходного массива.
    Но преобразование типа не может порождать новые объекты. Такие операции должны выполняться только явным образом с применением ключевого слова new. По этой причине преобразования типов массивов, основанных на примитивных типах, запрещены.
    Если же копирование элементов действительно требуется, то нужно сначала создать новый массив, а затем воспользоваться стандартной функцией System.arraycopy(), которая эффективно выполняет копирование элементов одного массива в другой.

    Приложение MenuApp

    Назад Вперед
    Автономное приложение MenuApp, работающее под управлением интерпертатора Java, демонстрирует способы создания меню. В его окне (рис. 1) имеется панель с меню File и Help.
    Приложение MenuApp
    Рис. 1. Главное окно автономного приложения MenuApp
    В меню File мы добавили строки New и Exit, а также разделитель в виде горизонтальной линии (рис. 2).
    Приложение MenuApp
    Рис. 2. Меню File
    Меню Help (рис. 3) содержит строки Content и About. Между ними также имеется разделительная линия.
    Приложение MenuApp
    Рис. 3. Меню Help
    Если выбрать любую строку, кроме строки Exit из меню File, на экране появится диалоговая панель с названием выбранной строки и кнопкой OK (рис. 4).
    Приложение MenuApp
    Рис. 4. Диалоговая панель, которая появляется при выборе строки New из меню File
    Выбор строки Exit из меню File приводит к завершению работы приложения MenuApp.
    Назад Вперед

    Приложение MenuAppПриложение MenuApp Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Приложение MenuApp
    printmenus();
    Приложение MenuAppПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Приложение MenuApp»


    Приложение MenuAppSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Приложение MenuApp»


    Приложение MenuAppКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Приложение MenuApp»


    Приложение MenuAppГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Приложение MenuApp»


    Приложение MenuAppОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Приложение MenuApp»


    Приложение MenuAppПроекты
    События
    Lab Downloads
    Посмотреть все Приложение MenuApp»


    Приложение MenuApp

    Применение класса Frame

    Для того чтобы создать свое окно на базе класса Frame, вы должны определить свой класс, унаследовав его от класса Frame следующим образом:
    class MainFrameWnd extends Frame { . . . public MainFrameWnd(String sTitle) { super(sTitle); . . . resize(400, 200); } . . . }
    Если мы будем создавать окно с заголовком, нам необходимо соответствующим образом определить конструктор класса этого окна. В частности, наш конструктор должен вызывать конструктор базового класса, передавая ему в качестве параметра строку заголовка окна. Напомним, что конструктор базового класса должен вызываться в конструкторе дочернего класса перед выполнением каких-либо других действий.
    Обратите также внимание на вызов метода resize. Этот вызов необходим для задания размеров окна.
    В конструкторе вы можете определить различные параметры создаваемого вами окна, например, указать форму курсора, пиктограмму, представляющую окно, задать меню, определить возможность изменения размеров окна и так далее. Мы остановимся подробнее на процедуре добавления меню к окну класса Frame, так как она требует пояснений. С изменением других характеристик окна вы справитесь самостоятельно.
    При создании окна классов Frame и Dialog для них устанавливается режим размещения BorderLayout. Если вам нужен другой режим размещения, необходимо установить его явным образом.
    Кроме того, созданное окно появится на экране только после вызова для него метода show.
    Убрать окно с экрана вы можете методом hide. Этот метод прячет окно, но оставляет в памяти все связанные с ним ресурсы, поэтому вы сможете вновь отобразить спрятанное окно, вызвав метод show.
    В отличие от метода hide, метод dispose удаляет окно и освобождает все связанные с ним ресурсы. Этот метод применяется для окончательного удаления окна с экрана и из памяти.
    Еще одно замечание касается обработки операции уничтожения окна при помощи двойного щелчка левой клавиши мыши по системному меню окна или при помощи кнопки уничтожения окна, расположенной в правой части заголовка.

    Работа с классом Menu

    Метод addSeparator используется для добавления в меню разделительной строки. Аналогичный результат достигается и при добавлении в меню стоки "-":
    mnHelp.add("-");
    Заметим, что вы можете просто добавлять в меню строки по их названию, пользуясь методом add(String label), либо добавлять в меню элементы класса MenuItem, вызывая метод add(MenuItem mi).
    Назад Вперед

    Работа с классом MenuРабота с классом Menu Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Работа с классом Menu
    printmenus();
    Работа с классом MenuПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Работа с классом Menu»


    Работа с классом MenuSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Работа с классом Menu»


    Работа с классом MenuКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Работа с классом Menu»


    Работа с классом MenuГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Работа с классом Menu»


    Работа с классом MenuОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Работа с классом Menu»


    Работа с классом MenuПроекты
    События
    Lab Downloads
    Посмотреть все Работа с классом Menu»


    Работа с классом Menu

    Работа с классом MenuBar

    Для формирования главного меню окна вы должны создать объект класса MenuBar с помощью конструктора, а затем добавить в него отдельные меню.
    Объект главного меню создается следующим образом:
    MenuBar mbMainMenuBar; mbMainMenuBar = new MenuBar();
    Отдельные меню создаются на базе класса Menu, например:
    Menu mnFile; Menu mnHelp; mnFile = new Menu("File"); mnHelp = new Menu("Help");
    Создав меню, вы должны добавить в них строки. Для этого нужно вызвать метод add, передав ему в качестве параметра текст строки меню, например:
    mnFile.add("New"); mnFile.add("-"); mnFile.add("Exit");
    mnHelp.add("Content"); mnHelp.add("-"); mnHelp.add("About");
    Далее сформированные меню добавляются в главное меню:
    mbMainMenuBar.add(mnFile); mbMainMenuBar.add(mnHelp);
    И, наконец, теперь можно устанавливать главное меню в окне класса, созданного на базе класса Frame:
    setMenuBar(mbMainMenuBar);
    Назад Вперед

    Работа с классом MenuBarРабота с классом MenuBar Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Работа с классом MenuBar
    printmenus();
    Работа с классом MenuBarПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Работа с классом MenuBar»


    Работа с классом MenuBarSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Работа с классом MenuBar»


    Работа с классом MenuBarКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Работа с классом MenuBar»


    Работа с классом MenuBarГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Работа с классом MenuBar»


    Работа с классом MenuBarОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Работа с классом MenuBar»


    Работа с классом MenuBarПроекты
    События
    Lab Downloads
    Посмотреть все Работа с классом MenuBar»


    Работа с классом MenuBar

    Создание диалоговых панелей

    Назад Вперед
    Диалоговые панели создаются на базе класса Dialog, краткое описание которого приведено ниже.

    В этой лекции было рассмотрено

    В этой лекции было рассмотрено устройство массивов в Java. Подобно массивам в других языках, они представляют собой набор значений одного типа. Основным свойством массива является длина, которая в Java может равняться нулю. В противном случае, массив обладает элементами в количестве, равном длине, к которым можно обратиться, используя индекс, изменяющийся от 0 до величины длины без единицы. Длина задается при создании массива и у созданного массива не может быть изменена. Однако она не входит в определение типа, а потому одна переменная может ссылаться на массивы одного типа с различной длиной.
    Создать массив можно с помощью ключевого слова new, поскольку все массивы, включая определенные на основе примитивных значений, имеют объектный тип. Другой способ – воспользоваться инициализатором и перечислить значения всех элементов. В первом случае элементы принимают значения по умолчанию (0, false, null).
    Особым образом в Java устроены многомерные массивы. Они, по сути, являются одномерными, основанными на массивах меньшей размерности. Такой подход позволяет единообразно работать с многомерными массивами. Также он дает возможность создавать не только "прямоугольные" массивы, но и массивы любой конфигурации.
    Хотя массив и является ссылочным типом, работа с ним зачастую имеет некоторые особенности. Рассматриваются правила приведения типа массива. Как для любого объектного типа, приведение к Object является расширяющим. Приведение массивов, основанных на ссылочных типах, во многом подчиняется обычным правилам. А вот примитивные массивы преобразовывать нельзя. С преобразованиями связано и возникновение ошибки ArrayStoreException, причина которой – невозможность точного отслеживания типов в преобразованном массиве для компилятора.
    В заключение рассматриваются последние случаи взаимосвязи типа переменной и ее значения.
    Наконец, изучается механизм клонирования, существующий с самых первых версий Java и позволяющий создавать точные копии объектов, если их классы позволяют это делать, реализуя интерфейс Cloneable. Поскольку стандартное клонирование порождает только один новый объект, это приводит к особым эффектам при работе с объектными полями классов и массивами.

    Программирование на Java

    Аплет Rectangles

    Назад Вперед
    В качестве примера многопоточного приложения мы приведем аплет Rectangles (рис. 1). Он создает три потока. Первый поток рисует в окне аплета прямоугольники случайного размера и цвета, второй - эллипсы, а третий управляет потоком рисования эллипсов.
    Аплет Rectangles
    Рис. 1. Окно аплета Rectangles
    Расположение прямоугольников и эллипсов также выбирается случайно.
    Назад Вперед

    Аплет RectanglesАплет Rectangles Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Аплет Rectangles
    printmenus();
    Аплет RectanglesПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Аплет Rectangles»


    Аплет RectanglesSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Аплет Rectangles»


    Аплет RectanglesКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Аплет Rectangles»


    Аплет RectanglesГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Аплет Rectangles»


    Аплет RectanglesОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Аплет Rectangles»


    Аплет RectanglesПроекты
    События
    Lab Downloads
    Посмотреть все Аплет Rectangles»


    Аплет Rectangles

    Блоки и локальные переменные

    Блок - это последовательность операторов, объявлений локальных классов или локальных переменных, заключенных в скобки. Область видимости локальных переменных и классов ограничена блоком, в котором они определены.
    Операторы в блоке выполняются слева направо, сверху вниз. Если все операторы (выражения) в блоке выполняются нормально, то и весь блок выполняется нормально. Если какой-либо оператор (выражение) завершается ненормально, то и весь блок завершается ненормально.
    Нельзя объявлять несколько локальных переменных с одинаковыми именами в пределах видимости блока. Приведенный ниже код вызовет ошибку времени компиляции.
    public class Test {
    public Test() { } public static void main(String[] args) { Test t = new Test(); int x; lbl: { int x = 0; System.out.println("x = " + x); } } }
    В то же время не следует забывать, что локальные переменные перекрывают видимость переменных-членов. Так, следующий пример отработает нормально.
    public class Test { static int x = 5; public Test() { } public static void main(String[] args) { Test t = new Test(); int x = 1; System.out.println("x = " + x); } }
    На консоль будет выведено x = 1.
    То же самое правило применимо к параметрам методов.
    public class Test { static int x; public Test() { } public static void main(String[] args) { Test t = new Test(); t.test(5); System.out.println("Member value x = " + x); } private void test(int x){ this.x = x + 5; System.out.println("Local value x = " + x); } }
    В результате работы этого примера на консоль будет выведено:
    Local value x = 5 Member value x = 10
    На следующем примере продемонстрируем, что область видимости локальной переменной ограничена областью видимости блока, или оператора, в пределах которого данная переменная объявлена.
    public class Test { static int x = 5; public Test() { } public static void main(String[] args) { Test t = new Test(); { int x = 1; System.out.println("First block x = " + x); } { int x = 2; System.out.println("Second block x =" + x); } System.out.print("For cycle x = "); for(int x =0;x<5;x++) { System.out.print(" " + x); } } }

    Данный пример откомпилируется без ошибок и на консоль будет выведен следующий результат:

    First block x = 1 Second block x =2 For cycle x = 0 1 2 3 4

    Следует помнить, что определение локальной переменной есть исполняемый оператор. Если задана инициализация переменой, то выражение исполняется слева направо и его результат присваивается локальной переменной. Использование неинициализированных локальных переменных запрещено и вызывает ошибку компиляции.

    Следующий пример кода

    public class Test { static int x = 5; public Test() { } public static void main(String[] args) { Test t = new Test(); int x; int y = 5; if( y > 3) x = 1; System.out.println(x); } }

    вызовет ошибку времени компиляции, т.к. возможны условия, при которых переменная x может быть не инициализирована до ее использования (несмотря на то, что в данном случае оператор if(y > 3) и следующее за ним выражение x = 1; будут выполняться всегда).

    Блокировка на заданный период времени

    С помощью метода sleep можно заблокировать поток на заданный период времени:
    try { Thread.sleep(500); } catch (InterruptedException ee) { . . . }
    В данном примере работа потока Thread приостанавливается на 500 миллисекунд. Заметим, что во время ожидания приостановленный поток не отнимает ресурсы процессора.
    Так как метод sleep может создавать исключение InterruptedException, необходимо предусмотреть его обработку. Для этого мы использовали операторы try и catch.

    Блокировка потока

    Синхронизированный поток, определенный как метод типа synchronized, может переходить в заблокированное состояние автоматически при попытке обращения к ресурсу, занятому другим синхронизированным методом, либо при выполнении некоторых операций ввода или вывода. Однако в ряде случаев полезно иметь более тонкие средства синхронизации, допускающие явное использование по запросу приложения.

    Цикл do

    Основная форма цикла do имеет следующий вид:
    do повторяющееся выражение или блок; while(логическое выражение)
    Цикл do будет выполняться до тех пор, пока логическое выражение будет истинным. В отличие от цикла while, этот цикл будет выполнен, как минимум, один раз.
    Типичная конструкция цикла do:
    int counter = 0; do { counter ++; System.out.println("Counter is " + counter); } while(counter < 5);
    В остальном выполнение цикла do аналогично выполнению цикла while, включая использование операторов break и continue.

    Цикл for

    Довольно часто бывает необходимо изменять значение какой-либо переменной в заданном диапазоне и выполнять повторяющуюся последовательность операторов с использованием этой переменной. Для выполнения такой последовательности действий как нельзя лучше подходит конструкция цикла for.
    Основная форма цикла for выглядит следующим образом:
    for(выражение инициализации; условие; выражение обновления) повторяющееся выражение или блок;
    Ключевыми элементами данной языковой конструкции являются предложения, заключенные в круглые скобки и разделенные точкой с запятой.
    Выражение инициализации выполняется до начала выполнения тела цикла. Чаще всего используется как некое стартовое условие (инициализация, или объявление переменной).
    Условие должно быть логическим выражением и трактуется точно так же, как логическое выражение в цикле while(). Тело цикла выполняется до тех пор, пока логическое выражение истинно. Как и в случае с циклом while(), тело цикла может не исполниться ни разу. Это происходит, если логическое выражение принимает значение "ложь" до начала выполнения цикла.
    Выражение обновления выполняется сразу после исполнения тела цикла и до того, как проверено условие продолжения выполнения цикла. Обычно здесь используется выражение инкрементации, но может быть применено и любое другое выражение.
    Пример использования цикла for():
    ... for(counter=0;counter<10;counter++) { System.out.println("Counter is " + counter); }
    В данном примере предполагается, что переменная counter была объявлена ранее. Цикл будет выполнен 10 раз и будут напечатаны значения счетчика от 0 до 9.
    Разрешается определять переменную прямо в предложении:
    for(int cnt = 0;cnt < 10; cnt++) { System.out.println("Counter is " + cnt); }
    Результат выполнения этой конструкции будет аналогичен предыдущему. Однако нужно обратить внимание, что область видимости переменной cnt будет ограничена телом цикла.
    Любая часть конструкции for() может быть опущена. В вырожденном случае мы получим оператор for с пустыми значениями
    for(;;) { ... }
    В данном случае цикл будет выполняться бесконечно. Эта конструкция аналогична конструкции while(true){}. Условия, в которых она может быть применена, мы рассмотрим позже.
    Возможно также расширенное использование синтаксиса оператора for(). Предложение и выражение могут состоять из нескольких частей, разделенных запятыми.
    for(i = 0, j = 0; i<5;i++, j+=2) { ... }
    Использование такой конструкции вполне правомерно.

    Цикл while

    Основная форма цикла while может быть представлена так:
    while(логическое выражение) повторяющееся выражение, или блок;
    В данной языковой конструкции повторяющееся выражение, или блок будет исполняться до тех пор, пока логическое выражение будет иметь истинное значение. Этот многократно исполняемый блок называют телом цикла
    Операторы continue и break могут изменять нормальное исполнение тела цикла. Так, если в теле цикла встретился оператор continue, то операторы, следующие за ним, будут пропущены и выполнение цикла начнется сначала. Если continue используется с меткой и метка принадлежит к данному while, то выполнение его будет аналогичным. Если метка не относится к данному while, его выполнение будет прекращено и управление будет передано на оператор, или блок, к которому относится метка.
    Если встретился оператор break, то выполнение цикла будет прекращено.
    Если выполнение блока было прекращено по какой-то другой причине (возникла исключительная ситуация), то выполнение всего цикла будет прекращено по той же причине.
    Рассмотрим несколько примеров:
    public class Test { static int x = 5; public Test() { } public static void main(String[] args) { Test t = new Test(); int x = 0; while(x < 5) { x++; if(x % 2 == 0) continue; System.out.print(" " + x); } } }
    На консоль будет выведено
    1 3 5
    т.е. вывод на печать всех четных чисел будет пропущен.
    public class Test { static int x = 5; public Test() { } public static void main(String[] args) { Test t = new Test(); int x = 0; int y = 0; lbl: while(y < 3) { y++; while(x < 5) { x++; if(x % 2 == 0) continue lbl; System.out.println("x=" + x + " y="+y); } } } }
    На консоль будет выведено
    x=1 y=1 x=3 y=2 x=5 y=3
    т.е. при выполнении условия if(x % 2 == 0) continue lbl; цикл по переменной x будет прерван, а цикл по переменной y начнет новую итерацию.
    Типичный вариант использования выражения while():
    int i = 0; while( i++ < 5) { System.out.println("Counter is " + i); }
    Следует помнить, что цикл while() будет выполнен только в том случае, если на момент начала его выполнения логическое выражение будет истинным. Таким образом, при выполнении программы может иметь место ситуация, когда цикл while() не будет выполнен ни разу.
    boolean b = false; while(b) { System.out.println("Executed"); }
    В данном случае строка System.out.println("Executed"); выполнена не будет.

    Именованные блоки

    В реальной практике достаточно часто используются вложенные циклы. Соответственно, может возникнуть ситуация, когда из вложенного цикла нужно прервать внешний. Простое использование break или continue не решает этой задачи, однако в Java можно именовать блок кода и явно указать операторам, к какому из них относится выполняемое действие. Делается это путем присвоения метки операторам do, while, for.
    Метка - это любая допустимая в данном контексте лексема, оканчивающаяся двоеточием.
    Рассмотрим следующий пример:
    ... int array[][] = {...}; for(int i=0;i<5;i++) { for(j=0;j<4; j++) { ... if(array[i][j] == caseValue) break; ... } } ...
    В данном случае при выполнении условия будет прервано выполнение цикла по j, цикл по i продолжится со следующего значения. Для того, чтобы прервать выполнение обоих циклов, используется метка:
    ... int array[][] = {:.}; outerLoop: for(int i=0;i<5;i++) { for(j=0;j<4; j++){ ... if(array[i][j] == caseValue) break outerLoop; ... } } ...
    Оператор break также может использоваться с именованными блоками.
    Между операторами break и continue есть еще одно существенное отличие. Оператор break может использоваться с любым именованным блоком, в этом случае его действие в чем-то похоже на действие goto. Оператор continue (как и отмечалось ранее) может быть использован только в теле цикла. То есть такая конструкция будет вполне приемлемой:
    lbl:{ ... if( val > maxVal) break lbl; ... }
    В то время как оператор continue здесь применять нельзя. В данном случае при выполнении условия if выполнение блока с меткой lbl будет прервано, то есть управление будет передано на оператор (выражение), следующий непосредственно за закрывающей фигурной скобкой.
    Метки используют пространство имен, отличное от пространства имен классов и методов.
    Так, следующий пример кода будет вполне работоспособным:
    public class Test { public Test() { } public static void main(String[] args) { Test t = new Test(); t.test(); } void test() { Test: { test: for(int i =0;true;i++) { if(i % 2 == 0) continue test; if(i > 10) break Test; System.out.print(i + " "); } } } }
    Для составления меток применяются те же синтаксические правила, что и для переменных, за тем исключением, что метки всегда оканчиваются двоеточием. Метки всегда должны быть привязаны к какому-либо блоку кода. Допускается использование меток с одинаковыми именами, но нельзя применять одинаковые имена в пределах видимости блока. Т.е. такая конструкция допустима:
    lbl: { ... System.out.println("Block 1"); ... } ... lbl: { ... System.out.println("Block 2"); ... }
    А такая нет:
    lbl: { ... lbl: { ... } ... }

    Исходные тексты аплета Rectangles

    Назад Вперед
    Исходные тексты аплета Rectangles приведены в листинге 1.
    Листинг 1. Файл Rectangles,java
    import java.applet.*; import java.awt.*;
    public class Rectangles extends Applet { DrawRectangles m_DrawRectThread = null; DrawEllipse m_DrawEllipseThread = null; NotifyTask m_NotifyTaskThread = null
    public String getAppletInfo() { return "Name: Rectangles"; }
    public void paint(Graphics g) { Dimension dimAppWndDimension = getSize();
    g.setColor(Color.yellow); g.fillRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);
    g.setColor(Color.black); g.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1); }
    public void start() { if (m_DrawRectThread == null) { m_DrawRectThread = new DrawRectangles(this); m_DrawRectThread.start(); }
    if (m_DrawEllipseThread == null) { m_DrawEllipseThread = new DrawEllipse(this); m_DrawEllipseThread.start(); }
    if (m_NotifyTaskThread == null) { m_NotifyTaskThread = new NotifyTask(m_DrawEllipseThread); m_NotifyTaskThread.start(); } }
    public void stop() { if (m_DrawRectThread != null) { m_DrawRectThread.stop(); m_DrawRectThread = null; }
    if (m_DrawEllipseThread == null) { m_DrawEllipseThread.stop(); m_DrawEllipseThread = null; }
    if (m_NotifyTaskThread != null) { m_NotifyTaskThread.stop(); m_NotifyTaskThread = null; } } }
    class DrawRectangles extends Thread { Graphics g; Dimension dimAppWndDimension;
    public DrawRectangles(Applet Appl) { g = Appl.getGraphics(); dimAppWndDimension = Appl.getSize(); }
    public void run() { while (true) { int x, y, width, height; int rColor, gColor, bColor;
    x = (int)(dimAppWndDimension.width * Math.random()); y = (int)(dimAppWndDimension.height * Math.random()); width = (int)(dimAppWndDimension.width * Math.random()) / 2; height = (int)(dimAppWndDimension.height * Math.random()) / 2;
    rColor = (int)(255 * Math.random()); gColor = (int)(255 * Math.random()); bColor = (int)(255 * Math.random());
    g.setColor(new Color(rColor, gColor, bColor)); g.fillRect(x, y, width, height);

    Использование оператора throw

    Помимо того, что предопределенная исключительная ситуация может быть возбуждена исполняющей системой Java, программист сам может явно породить ошибку. Делается это с помощью оператора throw.
    Например:
    ... public int calculate(int theValue) { if( theValue < 0) { throw new Exception( "Параметр для вычисления не должен быть отрицательным"); } } ...
    В данном случае предполагается, что в качестве параметра методу может быть передано только положительное значение; если это условие не выполнено, то с помощью оператора throw порождается исключительная ситуация. (Для успешной компиляции также требуется в заголовке метода указать throws Exception - это выражение рассматривается ниже.)
    Метод должен делегировать обработку исключительной ситуации вызвавшему его коду. Для этого в сигнатуре метода применяется ключевое слово throws, после которого должны быть перечислены через запятую все исключительные ситуации, которые может вызывать данный метод. То есть приведенный выше пример должен быть приведен к следующему виду:
    ... public int calculate(int theValue) throws Exception { if( theValue < 0) { throw new Exception( "Some descriptive info"); } } ...
    Таким образом, создание исключительной ситуации в программе выполняется с помощью оператора throw с аргументом, значение которого может быть приведено к типу Throwable.
    В некоторых случаях после обработки исключительной ситуации может возникнуть необходимость передать информацию о ней в вызывающий код.
    В этом случае ошибка появляется вторично.
    Например:
    ... try { ... } catch(IOException ex) { ... // Обработка исключительной ситуации ... // Повторное возбуждение исключительной // ситуации throw ex; }
    Рассмотрим еще один случай.
    Предположим, что оператор throw применяется внутри конструкции try-catch.
    try { ... throw new IOException(); ... } catch(Exception e) { ... }
    В этом случае исключение, возбужденное в блоке try, не будет передано для обработки на более высокий уровень иерархии, а обработается в пределах блока try-catch, так как здесь содержится оператор, который может это исключение перехватить. То есть произойдет неявная передача управления на соответствующий блок catch.

    Конструкция try-catch-finally

    Оператор finally предназначен для того, чтобы обеспечить гарантированное выполнение какого-либо фрагмента кода. Вне зависимости от того, возникла ли исключительная ситуация в блоке try, задан ли подходящий блок catch, не возникла ли ошибка в самом блоке catch,- все равно блок finally будет в конце концов исполнен.
    Последовательность выполнения такой конструкции следующая: если оператор try выполнен нормально, то будет выполнен блок finally. В свою очередь, если оператор finally выполняется нормально, то и весь оператор try выполняется нормально.
    Если во время выполнения блока try возникает исключение и существует оператор catch, который перехватывает данный тип исключения, происходит выполнение связанного с catch блока. Если блок catch выполняется нормально, либо ненормально, все равно затем выполняется блок finally. Если блок finally завершается нормально, то оператор try завершается так же, как завершился блок catch.
    Если в списке операторов catch не находится такого, который обработал бы возникшее исключение, то все равно выполняется блок finally. В этом случае, если finally завершится нормально, весь try завершится ненормально по той же причине, по которой было нарушено исполнение try.
    Во всех случаях, если блок finally завершается ненормально, то весь try завершится ненормально по той же причине.
    Рассмотрим пример применения конструкции try-catch-finally.
    try { byte [] buffer = new byte[128]; FileInputStream fis = new FileInputStream("file.txt"); while(fis.read(buffer) > 0) { ... обработка данных ... } } catch(IOException es) { ... обработка исключения ... } finally { fis.flush(); fis.close(); }
    Если в данном примере поместить операторы очистки буфера и закрытия файла сразу после окончания обработки данных, то при возникновении ошибки ввода/вывода корректного закрытия файла не произойдет. Еще раз отметим, что блок finally будет выполнен в любом случае, вне зависимости от того, произошла обработка исключения или нет, возникло это исключение или нет.

    В конструкции try-catch- finally обязательным является использование одной из частей оператора catch или finally. То есть конструкция

    try { ... } finally { ... }

    является вполне допустимой. В этом случае блок finally при возникновении исключительной ситуации должен быть выполнен, хотя сама исключительная ситуация обработана не будет и будет передана для обработки на более высокий уровень иерархии.

    Если обработка исключительной ситуации в коде не предусмотрена, то при ее возникновении выполнение метода будет прекращено и исключительная ситуация будет передана для обработки коду более высокого уровня. Таким образом, если исключительная ситуация произойдет в вызываемом методе, то управление будет передано вызывающему методу и обработку исключительной ситуации должен произвести он. Если исключительная ситуация возникла в коде самого высокого уровня (например, методе main()), то управление будет передано исполняющей системе Java и выполнение программы будет прекращено (более точно - будет остановлен поток исполнения, в котором произошла такая ошибка).

    Конструкция try-catch

    В общем случае конструкция выглядит так:
    try { ... } catch(SomeExceptionClass e) { ... } catch(AnotherExceptionClass e) { ... }
    Работает она следующим образом. Сначала выполняется код, заключенный в фигурные скобки оператора try. Если во время его выполнения не происходит никаких нештатных ситуаций, то далее управление передается за закрывающую фигурную скобку последнего оператора catch, ассоциированного с данным оператором try.
    Если в пределах try возникает исключительная ситуация, то далее выполнение кода производится по одному из перечисленных ниже сценариев.
    Возникла исключительная ситуация, класс которой указан в качестве параметра одного из блоков catch. В этом случае производится выполнение блока кода, ассоциированного с данным catch (заключенного в фигурные скобки). Далее, если код в этом блоке завершается нормально, то и весь оператор try завершается нормально и управление передается на оператор (выражение), следующий за закрывающей фигурной скобкой последнего catch. Если код в catch завершается не штатно, то и весь try завершается нештатно по той же причине.
    Если возникла исключительная ситуация, класс которой не указан в качестве аргумента ни в одном catch, то выполнение всего try завершается нештатно.

    Конструктор класса DrawRectangles

    В качестве параметра конструктору передается ссылка на класс аплета. Конструктор использует эту ссылку для получения и сохранения в полях класса контекста отображения и размеров окна аплета:
    public DrawRectangles(Applet Appl) { g = Appl.getGraphics(); dimAppWndDimension = Appl.getSize(); }

    Конструктор класса NotifyTask

    Конструктор класса NotifyTask записывает в поле STask ссылку на задачу рисования эллипсов:
    public NotifyTask(Thread SynchroTask) { STask = SynchroTask; }

    Конструкторы

    Создание нового объекта Thread
    public Thread();
    Создвание нового объекта Thread с указанием объекта, для которого будет вызываться метод run
    public Thread(Runnable target);
    Аналогично предыдущему, но дополнительно задается имя нового объекта Thread
    public Thread(Runnable target, String name);
    Создание объекта Thread с указанием его имени
    public Thread(String name);
    Создание нового объекта Thread с указанием группы потока и объекта, для которого вызывается метод run
    public Thread(ThreadGroup group, Runnable target);
    Аналогично предыдущему, но дополнительно задается имя нового объекта Thread
    public Thread(ThreadGroup group, Runnable target, String name);
    Создание нового объекта Thread с указанием группы потока и имени объекта
    public Thread(ThreadGroup group, String name);

    Метки

    Любой оператор, или блок, может иметь метку. Метку можно указывать в качестве параметра для операторов break и continue. Область видимости метки ограничивается оператором, или блоком, к которому она относится. Так, в следующем примере мы получим ошибку компиляции:
    public class Test { static int x = 5; static { } public Test() { } public static void main(String[] args) { Test t = new Test(); int x = 1; Lbl1: { if(x == 0) break Lbl1; }
    Lbl2:{ if(x > 0) break Lbl1; } } }
    В случае, если имеется несколько вложенных блоков и операторов, допускается обращение из внутренних блоков к меткам, относящимся к внешним.
    Этот пример является вполне корректным:
    public class Test { static int x = 5; static {
    } public Test() { } public static void main(String[] args) { Test t = new Test(); int L2 = 0; Test: for(int i = 0; i< 10;i++) { test: for(int j = 0; j< 10;j++) { if( i*j > 50) break Test; } } } private void test() { ; } }
    В этом же примере можно увидеть, что метки используют пространство имен, отличное от пространства имен переменных, методов и классов.
    Традиционно использование меток не рекомендуется, особенно в объектно-ориентированных языках, поскольку серьезно усложняет понимание порядка выполнения кода, а значит, и его тестирование и отладку. Для Java этот запрет можно считать не столь строгим, поскольку самый опасный оператор goto отсутствует. В некоторых ситуациях (как в рассмотренном примере с вложенными циклами) использование меток вполне оправданно, но, конечно, их применение следует ограничивать лишь самыми необходимыми случаями.

    Метод run класса DrawEllipse

    Класс DrawEllipse очень похож на только что рассмотренный класс DrawRectangles. Отличие есть только в финальном фрагменте метода run, который мы и рассмотрим.
    Вместо задержки на 50 миллисекунд метод run из класса DrawEllipse переходит в состояние ожидания извещения, вызывая метод wait:
    try { this.wait(); } catch (InterruptedException e) { }
    Это извещение создается управляющим потоком класса NotifyTask, к описанию которого мы переходим.

    Метод run класса DrawRectangles

    Программный код метода run работает в рамках отдельного потока. Он рисует в окне аплета закрашенные прямоугольники. Прямоугольники имеют случайные координаты, расположение и цвет.
    Для того чтобы рисовать, необходимо получить контекст отображения. Этот контекст был получен конструктором класса DrawRectangles и может быть использован методом run.
    Вооружившись контекстом отображения и размерами окна аплета, поток входит в бесконечный цикл рисования прямоугольников.
    В качестве генератора случайных чисел мы используем метод random из класса Math, который при каждом вызове возвращает новое случайное число типа double, лежащее в диапазоне значений от 0.0 до 1.0.
    Координаты по осям X и Y рисуемого прямоугольника определяются простым умножением случайного числа, полученного от метода random, соответственно, на ширину и высоту окна аплета:
    x = (int)(dimAppWndDimension.width * Math.random()); y = (int)(dimAppWndDimension.height * Math.random());
    Аналогично определяются размеры прямоугольника, однако чтобы прямоугольники не были слишком крупными, мы делим полученные значения на 2:
    width = (int)(dimAppWndDimension.width * Math.random()) / 2; height = (int)(dimAppWndDimension.height * Math.random()) / 2;
    Так как случайное число имеет тип double, в обоих случаях мы выполняем явное преобразование результата вычислений к типу int.
    Для случайного выбора цвета прямоугольника мы вычисляем отдельные цветовые компоненты, умножая значение, полученное от метода random, на число 255:
    rColor = (int)(255 * Math.random()); gColor = (int)(255 * Math.random()); bColor = (int)(255 * Math.random());
    Полученные значения цветовых компонент используются в конструкторе Color для получения цвета. Этот цвет устанавливается в контексте отображения методом setColor:
    g.setColor(new Color(rColor, gColor, bColor));
    Теперь все готово для рисования прямоугольника, которое мы выполняем при помощи метода fillRect:
    g.fillRect(x, y, width, height);
    После рисования прямоугольника метод run задерживает свою работу на 50 миллисекунд, вызывая метод sleep:
    try { Thread.sleep(50); } catch (InterruptedException e) { stop(); }
    Для обработки исключения InterruptedException, которое может возникнуть во время работы этого метода, мы предусмотрели блок try - catch. При возникновении указанного исключения работа потока останавливается вызовом метода stop.

    Метод run класса NotifyTask

    Метод run класса NotifyTask периодически разблокирует поток рисования эллипсов, вызывая для этого метод notify в цилке с задержкой 30 миллисекунд. Обращение к объекту STask, который хранит ссылку на поток рисования эллипсов, выполняется с использованием синхронизации:
    public void run() { while (true) { try { Thread.sleep(30); } catch (InterruptedException e) { }
    synchronized(STask) { STask.notify(); } } }
    Назад Вперед

    Метод run класса NotifyTaskМетод run класса NotifyTask Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Метод run класса NotifyTask
    printmenus();
    Метод run класса NotifyTaskПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Метод run класса NotifyTask»


    Метод run класса NotifyTaskSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Метод run класса NotifyTask»


    Метод run класса NotifyTaskКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Метод run класса NotifyTask»


    Метод run класса NotifyTaskГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Метод run класса NotifyTask»


    Метод run класса NotifyTaskОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Метод run класса NotifyTask»


    Метод run класса NotifyTaskПроекты
    События
    Lab Downloads
    Посмотреть все Метод run класса NotifyTask»


    Метод run класса NotifyTask

    Метод start класса Rectangles

    Этот метод последовательно создает три потока и запускает их на выполнение:
    if(m_DrawRectThread == null) { m_DrawRectThread = new DrawRectangles(this); m_DrawRectThread.start(); }
    if(m_DrawEllipseThread == null) { m_DrawEllipseThread = new DrawEllipse(this); m_DrawEllipseThread.start(); }
    if(m_NotifyTaskThread == null) { m_NotifyTaskThread = new NotifyTask(m_DrawEllipseThread); m_NotifyTaskThread.start(); }
    В качестве параметра конструкторам классов DrawRectangles и DrawEllipse мы передаем ссылку на аплет Rectangles. Эта ссылка будет нужна для получения контекста отображения и рисования геометрических фигур.
    Поток класса NotifyTask будет управлять работой потока DrawEllipse, поэтому мы передаем его конструктору ссылку на соответствующий объект m_DrawEllipseThread.

    Метод stop класса Rectangles

    Когда пользователь покидает страницу сервера Web с аплетом, метод stop класса Rectangles последовательно останавливает gjnjrb рисования прямоугольников и эллипсов, а также управляющий поток:
    if(m_DrawRectThread != null) { m_DrawRectThread.stop(); m_DrawRectThread = null; }
    if(m_DrawEllipseThread == null) { m_DrawEllipseThread.stop(); m_DrawEllipseThread = null; }
    if(m_NotifyTaskThread != null) { m_NotifyTaskThread.stop(); m_NotifyTaskThread = null; }

    Методы

  • activeCount

  • Текущее количество активных потоков в группе, к которой принадлежит поток
    public static int activeCount();
  • checkAccess

  • Текущему потоку разрешается изменять объект Thread
    public void checkAccesss();
  • countStackFrames

  • Определение количества фреймов в стеке
    public int countStackFrames();
  • currentThread

  • Определение текущего работающего потока
    public static Thread currentThread();
  • destroy

  • Принудительное завершение работы потока
    public void destroy();
  • dumpStack

  • Вывод текущего содержимого стека для отладки
    public static void dumpStack();
  • enumerate

  • Получение всех объектов Tread данной группы
    public static int enumerate(Thread tarray[]);
  • getName

  • Определение имени потока
    public final String getName();
  • getPriority

  • Определение текущего приоритета потока
    public final int getPriority();
  • getThreadGroup

  • Определение группы, к которой принадлежит поток
    public final ThreadGroup getThreadGroup();
  • interrupt

  • Прерывание потока
    public void interrupt();
  • interrupted

  • Определение, является ли поток прерванным
    public static boolean interrupted();
  • isAlive

  • Определение, выполняется поток или нет
    public final boolean isAlive();
  • isDaemon

  • Определение, является ли поток демоном
    public final boolean isDaemon();
  • isInterrupted

  • Определение, является ли поток прерванным
    public boolean isInterrupted();
  • join

  • Ожидание завершения потока
    public final void join();
    Ожидание завершения потока в течение заданного времени. Время задается в миллисекундах
    public final void join(long millis);
    Ожидание завершения потока в течение заданного времени. Время задается в миллисекундах и наносекундах
    public final void join(long millis, int nanos);
  • resume

  • Запуск временно приостановленного потока
    public final void resume();
  • run

  • Метод вызывается в том случае, если поток был создан как объект с интерфейсом Runnable
    public void run();
  • setDaemon

  • Установка для потока режима демона
    public final void setDaemon(boolean on);
  • setName


  • Устаовка имени потока

    public final void setName(String name);

  • setPriority


  • Установка приоритета потока

    public final void setPriority(int newPriority);

  • sleep


  • Задержка потока на заднное время. Время задается в миллисекундах и наносекундах

    public static void sleep(long millis);

    Задержка потока на заднное время. Время задается в миллисекундах и наносекундах

    public static void sleep(long millis, int nanos);

  • start


  • Запуск потока на выполнение

    public void start();

  • stop


  • Остановка выполнения потока

    public final void stop();

    Аварийная остановка выполнения потока с заданным исключением

    public final void stop(Throwable obj);

  • suspend


  • Приостановка потока

    public final void suspend();

  • toString


  • Строка, представляющая объект-поток

    public String toString();

  • yield


  • Приостановка текущего потока для того чтобы управление было передано другому потоку

    public static void yield();

    Методы класса Thread

    В классе Thread определены три поля, несколько конструкторов и большое количество методов, предназначенных для работы с потоками. Ниже мы привели краткое описание полей, конструкторов и методов.
    С помощью конструкторов вы можете создавать потоки различными способами, указывая при необходимости для них имя и группу. Имя предназначено для идентификации потока и является необязательным атрибутом. Что же касается групп, то они предназначены для организации защиты потоков друг от друга в рамках одного приложения.
    Методы класса Thread предоставляют все необходимые возможности для управления потоками, в том числе для их синхронизации.

    Многопоточность

    Назад Вперед
    Наверное, сегодня уже нет необходимости объяснять, что такое многопоточность. Все современные операционные системы, такие как Windows 95, Windows NT, OS/2 или UNIX способны работать в многопоточном режиме, повышая общую производительность системы за счет эффективного распараллеливания выполняемых потоков. Пока один поток находится в состоянии ожидания, например, завершения операции обмена данными с медленным периферийным устройством, другой может продолжать выполнять свою работу.
    Пользователи уже давно привыкли запускать параллельно несколько приложений для того чтобы делать несколько дел сразу. Пока одно из них занимается, например, печатью документа на принтере или приемом электронной почты из сети Internet, другое может пересчитывать электронную таблицу или выполнять другую полезную работу. При этом сами по себе запускаемые приложения могут работать в рамках одного потока - операционная система сама заботится о распределении времени между всеми запущенными приложениями.
    Создавая приложения для операционной системы Windows на языках программирования С или С++, вы могли решать многие задачи, такие как анимация или работа в сети, и без использования многопоточности. Например, для анимации можно было обрабатывать сообщения соответствующим образом настроенного таймера.
    Приложениям Java такая методика недоступна, так как в этой среде не предусмотрено способов периодического вызова каких-либо процедур. Поэтому для решения многих задач вам просто не обойтись без многопоточности.
    Назад Вперед

    МногопоточностьМногопоточность Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Многопоточность
    printmenus();
    МногопоточностьПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Многопоточность»


    МногопоточностьSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Многопоточность»


    МногопоточностьКаталог решений
    Истории успеха

    Нормальное и прерванное выполнение операторов

    Последовательность выполнения операторов может быть непрерывной, а может и прерываться (при возникновении определенных условий). Выполнение оператора может быть прервано, если в потоке вычислений будут обнаружены операторы
    break continue return
    Тогда управление будет передано в другое место (в соответствии с правилами обработки этих операторов, которые мы рассмотрим позже).
    Нормальное выполнение оператора может быть прервано также при возникновении исключительных ситуаций, которые тоже будут рассмотрены позднее. Явное возбуждение исключительной ситуации с помощью оператора throw также прерывает нормальное выполнение оператора и передает управление выполнением программы (далее просто управление) в другое место.
    Прерывание нормального исполнения всегда вызывается определенной причиной. Приведем список таких причин:
  • break (без указания метки);
  • break (с указанием метки);
  • continue (без указания метки);
  • continue (с указанием метки);
  • return (с возвратом значения);
  • return (без возврата значения);
  • throw с указанием объекта Throwable, а также все исключения, вызываемые виртуальной машиной Java.

  • Выражения могут завершаться нормально и преждевременно (аварийно). В данном случае термин "аварийно" вполне применим, т.к. причиной необычной последовательности выполнения выражения может быть только возникновение исключительной ситуации.
    Если в операторе содержится выражение, то в случае его аварийного завершения выполнение оператора тоже будет завершено преждевременно (т.е. нормальный ход выполнения оператора будет нарушен).
    В том случае, если в операторе имеется вложенный оператор и его завершение происходит ненормально, то так же ненормально завершается оператор, содержащий вложенный (в некоторых случаях это не так, что будет оговариваться особо).

    Оператор break

    Этот оператор, как и оператор continue, изменяет последовательность выполнения, но не возвращает исполнение к началу цикла, а прерывает его.
    public class Test { public Test() { } public static void main(String[] args) { Test t = new Test(); int [] x = {1,2,4,0,8}; int y = 8; for(int cnt=0;cnt < x.length;cnt++) { if(0 == x[cnt]) break; System.out.println("y/x = " + y/x[cnt]); } } }
    На консоль будет выведено:
    y/x = 8 y/x = 4 y/x = 2
    При этом ошибки, связанной с делением на ноль, не произойдет, т.к. если значение элемента массива будет равно 0, то будет выполнено условие в строке 9 и выполнение цикла for будет прервано.
    В качестве аргумента break может быть указана метка. Как и в случае с continue, нельзя указывать в качестве аргумента метки блоков, в которых оператор break не содержится.

    Оператор continue

    Оператор continue может использоваться только в циклах while, do, for. Если в потоке вычислений встречается оператор continue, то выполнение текущей последовательности операторов (выражений) должно быть прекращено и управление будет передано на начало блока, содержащего этот оператор.
    ... int x = (int)(Math.random()*10); int arr[10] = {....} for(int cnt=0;cnt<10;cnt++) { if(arr[cnt] == x) continue; ... }
    В данном случае, если в массиве arr встретится значение, равное x, то выполнится оператор continue и все операторы до конца блока будут пропущены, а управление будет передано на начало цикла.
    Если оператор continue будет применен вне контекста оператора цикла, то будет выдана ошибка времени компиляции. В случае использования вложенных циклов оператору continue, в качестве адреса перехода, может быть указана метка, относящаяся к одному из этих операторов.
    Рассмотрим пример:
    public class Test { public Test() { } public static void main(String[] args) { Test t = new Test(); for(int i=0; i < 10; i++){ if(i % 2 == 0) continue; System.out.print(" i=" + i); } } }
    В результате работы на консоль будет выведено:
    i=1 i=3 i=5 i=7 i=9
    При выполнении условия в строке 7 нормальная последовательность выполнения операторов будет прервана и управление будет передано на начало цикла. Таким образом, на консоль будут выводиться только нечетные значения.

    Оператор if

    Пожалуй, наиболее распространенной конструкцией в Java, как и в любом другом структурном языке программирования, является оператор условного перехода.
    В общем случае конструкция выглядит так:
    if (логическое выражение) выражение или блок 1 else выражение или блок 2
    Логическое выражение может быть любой языковой конструкцией, которая возвращает булевский результат. Отметим отличие от языка C, в котором в качестве логического выражения могут использоваться различные типы данных, где отличное от нуля выражение трактуется как истинное значение, а ноль - как ложное. В Java возможно использование только логических выражений.
    Если логическое выражение принимает значение "истина", то выполняется выражение или блок 1, в противном случае - выражение или блок 2. Вторая часть оператора (else) не является обязательной и может быть опущена. Т.е. конструкция if(x == 5) System.out.println("Five") вполне допустима.
    Операторы if-else могут каскадироваться.
    String test = "smb"; if( test.equals("value1") { ... } else if (test.equals("value2") { ... } else if (test.equals("value3") { ... } else { ... }
    Следует помнить, что оператор else относится к ближайшему к нему оператору if. В данном случае последнее условие else будет выполняться, только если не выполнено предыдущее. Заключительная конструкция else относится к самому последнему условию if и будет выполнена только в том случае, если ни одно из вышеперечисленных условий не будет истинным. Если хотя бы одно из условий выполнено, то все последующие выполняться не будут.
    Например:
    ... int x = 5; if( x < 4) { System.out.println("Меньше 4"); } else if (x > 4) { System.out.println("Больше 4"); } else if (x == 5) { System.out.println("Равно 5"); } else{ System.out.println("Другое значение"); }
    Предложение "Равно 5" в данном случае напечатано не будет.

    Оператор return

    Этот оператор предназначен для возврата управления из вызываемого метода в вызывающий. Если в последовательности операторов выполняется return, то управление немедленно (если это не оговорено особо) передается в вызывающий метод. Оператор return может иметь, а может и не иметь аргументов. Если метод не возвращает значений (объявлен как void), то в этом и только этом случае выражение return применяется без аргументов. Если возвращаемое значение есть, то return обязательно должен применяться с аргументом, чье значение и будет возвращено.
    В качестве аргумента return может использоваться выражение
    return (x*y +10) /11;
    В этом случае сначала будет выполнено выражение, а затем результат его выполнения будет передан в вызывающий метод. Если выражение будет завершено ненормально, то и оператор return будет завершен ненормально. Например, если во время выполнения выражения в операторе return возникнет исключение, то никакого значения метод не вернет, будет обрабатываться ошибка.
    В методе может быть более одного оператора return.

    Оператор switch

    Оператор switch() удобно использовать в случае необходимости множественного выбора. Выбор осуществляется на основе целочисленного значения.
    Структура оператора:
    switch(int value) { case const1: выражение или блок case const2: выражение или блок case constn: выражение или блок default: выражение или блок }
    Причем, фраза default не является обязательной.
    В качестве параметра switch может использоваться переменная типа byte, short, int, char или выражение. Выражение должно в конечном итоге возвращать параметр одного из указанных ранее типов. В операторе case не могут применяться значения примитивного типа long и ссылочных типов Long, String, Integer, Byte и т.д.
    При выполнении оператора switch производится последовательное сравнение значения x с константами, указанными после case, и в случае совпадения выполняется выражение следующего за этим условием. Если выражение выполнено нормально и нет преждевременного его завершения, то производится сравнение для последующих case. Если же выражение, следующее за case, завершилось ненормально, то будет прекращено выполнение всего оператора switch.
    Если не выполнен ни один оператор case, то выполнится оператор default, если он имеется в данном switch. Если оператора default нет и ни одно из условий case не выполнено, то ни одно из выражений switch также выполнено не будет.
    Следует обратить внимание, что, в отличие от многозвенного if-else, если какое-либо условие case выполнено, то выполнение switch не прекратится, а будут проверяться следующие за ним условия. Если этого необходимо избежать, то после кода следующего за оператором case используется оператор break, прерывающий дальнейшее выполнение оператора switch.
    После оператора case должен следовать литерал, который может быть интерпретирован как 32-битовое целое значение. Здесь не могут применяться выражения и переменные, если они не являются final static.
    Рассмотрим пример:
    int x = 2; switch(x) { case 1: case 2: System.out.println("Равно 1 или 2"); break; case 3: case 4: System.out.println("Равно 3 или 4"); break; default: System.out.println( "Значение не определено"); }
    В данном случае на консоль будет выведен результат "Равно 1 или 2". Если же убрать операторы break, то будут выведены все три строки.
    Вот такая конструкция вызовет ошибку времени компиляции.
    int x = 5; switch(x) { case y: // только константы! ... break; }
    В операторе switch не может быть двух case с одинаковыми значениями.
    Т.е. конструкция
    switch(x) { case 1: System.out.println("One"); break; case 1: System.out.println("Two"); break; case 3: System.out.println("Tree or other value"); }
    недопустима.
    Также в конструкции switch может быть применен только один оператор default.

    Оператор synchronized

    Этот оператор применяется для исключения взаимного влияния нескольких потоков при выполнении кода, он будет подробно рассмотрен в лекции 12, посвященной потокам исполнения.

    Операторы break и continue

    В некоторых случаях требуется изменить ход выполнения программы. В традиционных языках программирования для этих целей применяется оператор goto, однако в Java он не поддерживается. Для этих целей применяются операторы break и continue.

    Описание исходных текстов аплета Rectangles

    Назад Вперед
    В этом приложении мы создаем на базе класса Thread три класса. Первый из них предназначен для создания потока рисования прямоугольников, второй - для создания потока рисования закрашенных эллипсов, а третий - для управления потоком рисования эллипсов.
    Что же касается основного класса аплета, то он унаследован, как обычно, от класса Applet и не реализует интерфейс Runnable:
    public class Rectangles extends Applet { . . . }

    Ошибки при работе программы. Исключения (Exceptions)

    При выполнении программы могут возникать ошибки. В одних случаях это вызвано ошибками программиста, в других - внешними причинами. Например, может возникнуть ошибка ввода/вывода при работе с файлом или сетевым соединением. В классических языках программирования, например, в С, требовалось проверять некое условие, которое указывало на наличие ошибки, и в зависимости от этого предпринимать те или иные действия.
    Например:
    ... int statusCode = someAction(); if (statusCode){ ... обработка ошибки } else { statusCode = anotherAction(); if(statusCode) { ... обработка ошибки ... } } ...
    В Java появилось более простое и элегантное решение - обработка исключительных ситуаций.
    try{ someAction(); anotherAction(); } catch(Exception e) { // обработка исключительной ситуации }
    Легко заметить, что такой подход является не только изящным, но и более надежным и простым для понимания.

    Особые случаи

    Во время исполнения кода могут возникать ситуации, которые почти не описаны в литературе.
    Рассмотрим такую ситуацию:
    import java.io.*; public class Test {
    public Test() { } public static void main(String[] args) { Test test = new Test(); try { test.doFileInput("bogus.file"); } catch (IOException ex) { System.out.println("Second exception handle stack trace"); ex.printStackTrace(); } }
    private String doFileInput(String fileName) throws FileNotFoundException,IOException { String retStr = ""; java.io.FileInputStream fis = null; try { fis = new java.io.FileInputStream(fileName); } catch (FileNotFoundException ex) { System.out.println("First exception handle stack trace"); ex.printStackTrace(); throw ex; } return retStr; } }
    Результат работы будет выглядеть следующим образом:
    java.io.FileNotFoundException: bogus.file (The system cannot find the file specified) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.(FileInputStream.java:64) at experiment.Test.doFileInput(Test.java:33) at experiment.Test.main(Test.java:21) First exception handle stack trace java.io.FileNotFoundException: bogus.file (The system cannot find the file specified) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.(FileInputStream.java:64) at experiment.Test.doFileInput(Test.java:33) at experiment.Test.main(Test.java:21) Second exception handle stack trace
    Так как при вторичном возбуждении используется один и тот же объект Exception, стек в обоих случаях будет содержать одну и ту же последовательность вызовов. То есть при повторном возбуждении исключения, если мы используем тот же объект, изменения его параметров не происходит.
    Рассмотрим другой пример:
    import java.io.*;
    public class Test {
    public Test() { } public static void main(String[] args) { Test test = new Test(); try { test.doFileInput(); } catch (IOException ex) { System.out.println("Exception hash code " + ex.hashCode()); ex.printStackTrace(); } }

    private String doFileInput() throws FileNotFoundException,IOException{ String retStr = ""; java.io.FileInputStream fis = null; try { fis = new java.io.FileInputStream("bogus.file"); } catch (FileNotFoundException ex) { System.out.println("Exception hash code " + ex.hashCode()); ex.printStackTrace(); fis = new java.io.FileInputStream("anotherBogus.file"); throw ex; } return retStr; } }

    java.io.FileNotFoundException: bogus.file ( The system cannot find the file specified) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.(FileInputStream.java:64) at experiment.Test.doFileInput(Test.java:33) at experiment.Test.main(Test.java:21) Exception hash code 3214658

    java.io.FileNotFoundException: (The system cannot find the path specified) at java.io.FileInputStream.open(Native Method) at java.io.FileInputStream.(FileInputStream.java:64) at experiment.Test.doFileInput(Test.java:38) at experiment.Test.main(Test.java:21) Exception hash code 6129586

    Несложно заметить, что, хотя последовательность вызовов одна и та же, в вызываемом и вызывающем методах обрабатываются разные объекты исключений.

    Ожидание извещения

    Если вам нужно организовать взаимодействие потоков таким образом, чтобы один поток управлял работой другого или других потоков, вы можете воспользоваться методами wait, notify и notifyAll, определенными в классе Object.
    Метод wait может использоваться либо с параметром, либо без параметра. Этот метод переводит поток в состояние ожидания, в котором он будет находиться до тех пор, пока для потока не будет вызван извещающий метод notify, notifyAll, или пока не истечет период времени, указанный в параметре метода wait.
    Как пользоваться методами wait, notify и notifyAll?
    Метод, который будет переводиться в состояние ожидания, должен быть синхронизированным, то есть его следует описать как synchronized:
    public synchronized void run() { while (true) { . . . try { this.wait(); } catch (InterruptedException e) { } } }
    В этом примере внутри метода run определен цикл, вызывающий метод wait без параметров. Каждый раз при очередном проходе цикла метод run переводится в состояние ожидания до тех пор, пока другой поток не выполнит извещение с помощью метода notify.
    Ниже мы привели пример потока, вызывающией метод notify:
    public void run() { while (true) { try { Thread.sleep(30); } catch (InterruptedException e) { }
    synchronized(STask) { STask.notify(); } } }
    Этот поток реализован в рамках отдельного класса, конструктору которого передается ссылка на поток, вызывающую метод wait. Эта ссылка хранится в поле STask.
    Обратите внимание, что хотя сам метод run не синхронизированный, вызов метода notify выполняется в синхронизированном режиме. В качестве объекта синхронизации выступает поток, для которого вызывается метод notify.

    Ожидание завершения потока

    С помощью метода join вы можете выполнять ожидание завершения работы потока, для которой этот метод вызван.
    Существует три определения метода join:
    public final void join(); public final void join(long millis); public final void join(long millis, int nanos);
    Первый из них выполняет ожидание без ограничения во времени, для второго ожидание будет прервано принудительно через millis миллисекунд, а для третьего - через millis миллисекунд и nanos наносекунд. Учтите, что реально вы не сможете указывать время с точностью до наносекунд, так как дискретность системного таймера компьютера намного больше.
    Назад Вперед

    Ожидание завершения потокаОжидание завершения потока Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Ожидание завершения потока
    printmenus();
    Ожидание завершения потокаПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Ожидание завершения потока»


    Ожидание завершения потокаSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Ожидание завершения потока»


    Ожидание завершения потокаКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Ожидание завершения потока»


    Ожидание завершения потокаГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Ожидание завершения потока»


    Ожидание завершения потокаОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Ожидание завершения потока»


    Ожидание завершения потокаПроекты
    События
    Lab Downloads
    Посмотреть все Ожидание завершения потока»


    Ожидание завершения потока

    Переопределение методов и исключения

    При переопределении методов следует помнить, что если переопределяемый метод объявляет список возможных исключений, то переопределяющий метод не может расширять этот список, но может его сужать. Рассмотрим пример:
    public class BaseClass{ public void method () throws IOException { ... } }
    public class LegalOne extends BaseClass { public void method () throws IOException { ... } }
    public class LegalTwo extends BaseClass { public void method () { ... } }
    public class LegalTree extends BaseClass { public void method () throws EOFException,MalformedURLException { ... } }
    public class IllegalOne extends BaseClass { public void method () throws IOException,IllegalAccessException { ... } }
    public class IllegalTwo extends BaseClass { public void method () { ... throw new Exception(); } }
    В данном случае:
  • определение класса LegalOne будет корректным, так как переопределение метода method() верное (список ошибок не изменился);
  • определение класса LegalTwo будет корректным, так как переопределение метода method() верное (новый метод не может выбрасывать ошибок, а значит, не расширяет список возможных ошибок старого метода);
  • определение класса LegalTree будет корректным, так как переопределение метода method() будет верным (новый метод может создавать исключения, которые являются подклассами исключения, возбуждаемого в старом методе, то есть список сузился);
  • определение класса IlegalOne будет некорректным, так как переопределение метода method() неверно (IllegalAccessException не является подклассом IOException, список расширился);
  • определение класса IlegalTwo будет некорректным: хотя заголовок method() объявлен верно (список не расширился), в теле метода бросается исключение, не указанное в throws.


  • Поля

    Три статических поля предназначены для назначения приоритетов потокам.
  • NORM_PRIORITY

  • Нормальный
    public final static int NORM_PRIORITY;
  • MAX_PRIORITY

  • Максимальный
    public final static int MAX_PRIORITY;
  • MIN_PRIORITY

  • Минимальный
    public final static int MIN_PRIORITY;

    Поля класса DrawRectangles

    Класс DrawRectangles определен для потока рисования прямоугольников:
    class DrawRectangles extends Thread { . . . }
    В поле g класа хранится контекст отображения окна аплета, а в поле dimAppWndDimension - размеры этого окна:
    Graphics g; Dimension dimAppWndDimension;
    Значения этих полей определяются конструктором класса по ссылке на главный класс аплета.

    Поля класса NotifyTask

    В классе NotifyTask мы определили одно поле STask класса Thread. Это поле которое хранит ссылку на поток, работой которого управляет данный класс:
    class NotifyTask extends Thread { Thread STask; . . . }

    Поля класса Rectangles

    В классе Rectangles мы определили три поля с именами m_DrawRectThread, m_DrawEllipseThread и m_NotifyTaskThread:
    DrawRectangles m_DrawRectThread = null; DrawEllipse m_DrawEllipseThread = null; NotifyTask m_NotifyTaskThread = null
    Эти поля являются ссылками на классы, соответственно DrawRectangles, DrawEllipse и NotifyTask . Первый из них создан для рисования прямоугольников, второй - эллипсов, а третий - для управления потоком рисования эллипсов.
    Указанные поля инициализируются занчением null, что соответствует неработающим или несозданным задачам.

    Поток

    Для каждого процесса операционная система создает один главный поток (thread ), который является потоком выполняющихся по очереди команд центрального процессора. При необходимости главный поток может создавать другие потоки, пользуясь для этого программным интерфейсом операционной системы.
    Все потоки, созданные процессом, выполняются в адресном пространстве этого процесса и имеют доступ к ресурсам процесса. Однако поток одного процесса не имеет никакого доступа к ресурсам потока другого процесса, так как они работают в разных адресных пространствах. При необходимости организации взаимодействия между процессами или потоками, принадлежащими разным процессам, следует пользоваться системными средствами, специально предназначенными для этого.

    Потоки-демоны

    Назад Вперед
    Вызвав для потока метод setDaemon, вы превращаете обычную поток в поток-демон. Такой поток работает в фоновом режиме независимо от породившего его потока. Если поток-демон создает другие потоки, то они также станут получат статус потока-демона.
    Заметим, что метод setDaemon необходимо вызывать после создания потока, но до момента его запуска, то есть перед вызовом метода start.
    С помощью метода isDaemon вы можете проверить, является поток демоном, или нет.
    Назад Вперед

    Потоки-демоныПотоки-демоны Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Потоки-демоны
    printmenus();
    Потоки-демоныПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Потоки-демоны»


    Потоки-демоныSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Потоки-демоны»


    Потоки-демоныКаталог решений
    Истории успеха
    The Sun Grid
    Партнерские программы
    Посмотреть все Потоки-демоны»


    Потоки-демоныГарантийное обслуживание
    Программы SunSpectrum
    Консалтинг
    Услуги инсталляции
    Поддержка ПО
    Посмотреть все Потоки-демоны»


    Потоки-демоныОписание курсов
    Сертификация
    Авторизованные учебные центры
    Посмотреть все Потоки-демоны»


    Потоки-демоныПроекты
    События
    Lab Downloads
    Посмотреть все Потоки-демоны»


    Потоки-демоны

    Причины возникновения ошибок

    Существует три причины возникновения исключительных ситуаций.
  • Попытка выполнить некорректное выражение. Например, деление на ноль, или обращение к объекту по ссылке, равной null, попытка использовать класс, описание которого (class-файл) отсутствует, и т.д. В таких случаях всегда можно точно указать, в каком месте произошла ошибка, - именно в некорректном выражении.
  • Выполнение оператора throw Этот оператор применяется для явного порождения ошибки. Очевидно, что и здесь можно указать место возникновения исключительной ситуации.
  • Асинхронные ошибки во время исполнения программы.

  • Причиной таких ошибок могут быть сбои внутри самой виртуальной машины (ведь она также является программой), или вызов метода stop() у потока выполнения Thread.
    В этом случае невозможно указать точное место программы, где происходит исключительная ситуация. Если мы попытаемся остановить поток выполнения (вызвав метод stop()), нам не удастся предсказать, при выполнении какого именно выражения этот поток остановится.
    Таким образом, все ошибки в Java делятся на синхронные и асинхронные. С первыми сравнительно проще работать, так как принципиально возможно найти точное место в коде, которое является причиной возникновения исключительной ситуации. Конечно, Java является строгим языком в том смысле, что все выражения до точки сбоя обязательно будут выполнены, и в то же время ни одно последующее выражение никогда выполнено не будет. Важно помнить, что ошибки могут возникать как по причине недостаточной внимательности программиста (отсутствует нужный класс, или индекс массива вышел за допустимые границы), так и по независящим от него причинам (произошел разрыв сетевого соединения, сбой аппаратного обеспечения, например, жесткого диска и др.).
    Асинхронные ошибки гораздо сложнее в обнаружении и исправлении. Обычному разработчику очень трудно выявить причины сбоев в виртуальной машине. Это могут быть ошибки создателей JVM, несовместимость с операционной системой, аппаратный сбой и многое другое. Все же современные виртуальные машины реализованы довольно хорошо и подобные сбои происходят крайне редко (при условии использования качественных комплектующих).

    Аналогичная ситуация наблюдается и в случае с принудительной остановкой потоков исполнения. Поскольку это действие выполняется операционной системой, никогда нельзя предсказать, в каком именно месте остановится поток. Это означает, что программа может многократно отработать корректно, а потом неожиданно дать сбой просто из-за того, что поток остановился в каком-то другом месте. По этой причине принудительная остановка не рекомендуется. В лекции 12 рассматриваются примеры корректного управления жизненным циклом потока.

    При возникновении исключительной ситуации управление передается от кода, вызвавшего исключительную ситуацию, на ближайший блок catch (или вверх по стеку) и создается объект, унаследованный от класса Throwable, или его потомков (см. диаграмму иерархии классов-исключений), который содержит информацию об исключительной ситуации и используется при ее обработке. Собственно, в блоке catch указывается именно класс обрабатываемой ситуации. Подробно обработка ошибок рассматривается ниже.

    Иерархия, по которой передается информация об исключительной ситуации, зависит от того, где эта исключительная ситуация возникла. Если это

  • метод, то управление будет передаваться в то место, где данный метод был вызван;
  • конструктор, то управление будет передаваться туда, где попытались создать объект (как правило, применяя оператор new);
  • статический инициализатор, то управление будет передано туда, где произошло первое обращение к классу, потребовавшее его инициализации.


  • Допускается создание собственных классов исключительных ситуаций. Осуществляется это с помощью механизма наследования, то есть класс пользовательской исключительной ситуации должен быть унаследован от класса Throwable, или его потомков.

    Применение многопоточности для анимации

    Назад Вперед
    Одно из наиболее распространенных применений аплетов - это создание анимационных эффектов типа бегущей строки, мерцающих огней или аналогичных, привлекающих внимание пользователя. Для того чтобы достичь такого эффекта, необходим какой либо механизм, позволяющий выполнять перерисовку всего окна аплета или его части периодически с заданным временным интервалом.
    Работа аплетов, так же как и обычных приложений операционной системы Windows, основана на обработке событий. Для классического приложения Windows событие - это приход сообщения в функцию окна. Основной класс аплета обрабатывает события, переопределяя те или иные методы базового класса Applet.
    Проблема с периодическим обновлением окна аплета возникает из-за того, что в языке Java не предусмотрено никакого механизма для создания генератора событий, способного вызывать какой-либо метод класса аплета с заданным интервалом времени. Вы не можете поступить так, как поступали в этой ситуации, разрабатывая обычные приложения Windows - создать таймер и организовать обработку периодически поступающих от него сообщений WM_TIMER.
    Напомним, что перерисовка окна аплета выполняется методом paint, который вызывается виртуальной машиной Java асинхронно по отношению к выполнению другого кода аплета.
    Можно ли воспользоваться методом paint для периодической перерисовки окна аплета, организовав в нем, например, бесконечный цикл с задержкой?
    К сожалению, так поступать ни в коем случае нельзя. Метод paint после перерисовки окна аплета должен сразу возвратить управление, иначе работа аплета будет заблокирована.
    Единственный выход из создавшейся ситуации - создание потока (или нескльких потоков), которые будут выполнять рисование в окне аплета асинхронно по отношению к работе кода аплета. Например, вы можете создать поток, который периодически обновляет окно аплета, вызывая для этого метод repaint, или рисовать из потока непосредственно в окне аплета, получив предварительно для этого окна контекст отображения.

    Приоритеты потоков в приложениях Java

    Если процесс создал несколько потоков, то все они выполняются параллельно, причем время центрального процессора (или нескольких центральных процессоров в мультипроцессорных системах) распределяется между этими потоками.
    Распределением времени центрального процессора занимается специальный модуль операционной системы - планировщик. Планировщик по очереди передает управление отдельным потокам, так что даже в однопроцессорной системе создается полная иллюзия параллельной работы запущенных потоков.
    Распределение времени выполняется по прерываниям системного таймера. Поэтому каждому потоку дается определенный интервал времени, в течении которого он находится в активном состоянии.
    Заметим, что распределение времени выполняется для потоков, а не для процессов. Потоки, созданные разными процессами, конкурируют между собой за получение процессорного времени.
    Каким именно образом?
    Приложения Java могут указывать три значения для приоритетов потоков. Это NORM_PRIORITY, MAX_PRIORITY и MIN_PRIORITY.
    По умолчанию вновь созданный поток имеет нормальный приоритет NORM_PRIORITY. Если остальные потоки в системе имеют тот же самый приоритет, то все потоки пользуются процессорным времени на равных правах.
    При необходимости вы можете повысить или понизить приоритет отдельных потоков, определив для них значение приоритета, соответственно, MAX_PRIORITY или MIN_PRIORITY. Потоки с повышенным приоритетом выполняются в первую очередь, а с пониженным - только при отсутствии готовых к выполнению потоков, имеющих нормальный или повышенный приоритет.
    Назад Вперед

    Приоритеты потоков в приложениях JavaПриоритеты потоков в приложениях Java Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Приоритеты потоков в приложениях Java
    printmenus();
    Приоритеты потоков в приложениях JavaПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Приоритеты потоков в приложениях Java»


    Приоритеты потоков в приложениях JavaSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Приоритеты потоков в приложениях Java»


    Приоритеты потоков в приложениях JavaКаталог решений

    Процесс

    Процесс (process) - это объект, который создается операционной системой, когда пользователь запускает приложение. Процессу выделяется отдельное адресное пространство, причем это пространство физически недоступно для других процессов. Процесс может работать с файлами или с каналами связи локальной или глобальной сети. Когда вы запускаете текстовый процессор или программу калькулятора, вы создаете новый процесс.

    Процессы, потоки и приоритеты

    Назад Вперед
    Прежде чем приступить к разговору о многопоточности, следует уточнить некоторые термины.
    Обычно в любой многопоточной операционной системе выделяют такие объекты, как процессы и потоки. Между ними существует большая разница, которую следует четко себе представлять.

    Проверяемые и непроверяемые исключения

    Все исключительные ситуации можно разделить на две категории: проверяемые (checked) и непроверяемые (unchecked).
    Все исключения, порождаемые от Throwable, можно разбить на три группы. Они определяются тремя базовыми типами: наследниками Throwable - классами Error и Exception, а также наследником Exception - RuntimeException.
    Ошибки, порожденные от Exception (и не являющиеся наследниками RuntimeException), являются проверяемыми. Т.е. во время компиляции проверяется, предусмотрена ли обработка возможных исключительных ситуаций. Как правило, это ошибки, связанные с окружением программы (сетевым, файловым вводом-выводом и др.), которые могут возникнуть вне зависимости от того, корректно написан код или нет. Например, открытие сетевого соединения или файла может привести к возникновению ошибки и компилятор требует от программиста предусмотреть некие действия для обработки возможных проблем. Таким образом повышается надежность программы, ее устойчивость при возможных сбоях.
    Исключения, порожденные от RuntimeException, являются непроверяемыми и компилятор не требует обязательной их обработки.
    Как правило, это ошибки программы, которые при правильном кодировании возникать не должны (например, IndexOutOfBoundsException - выход за границы массива, java.lang.ArithmeticException - деление на ноль). Поэтому, чтобы не загромождать программу, компилятор оставляет на усмотрение программиста обработку таких исключений с помощью блоков try-catch.
    Исключения, порожденные от Error, также не являются проверяемыми. Они предназначены для того, чтобы уведомить приложение о возникновении фатальной ситуации, которую программным способом устранить практически невозможно (хотя формально обработчик допускается). Они могут свидетельствовать об ошибках программы, но, как правило, это неустранимые проблемы на уровне JVM. В качестве примера можно привести StackOverflowError (переполнение стека), OutOfMemoryError (нехватка памяти).
    Если в конструкции обработки исключений используется несколько операторов catch, классы исключений нужно перечислять в них последовательно, от менее общих к более общим. Рассмотрим два примера:
    try { ... } catch(Exception e) { ... } catch(IOException ioe) { ... } catch(UserException ue) { ... }

    Проверяемые и непроверяемые исключения
    Рис. 10.1.  Иерархия классов стандартных исключений.

    В данном примере при возникновении исключительной ситуации (класс, порожденный от Exception) будет выполняться всегда только первый блок catch. Остальные не будут выполнены ни при каких условиях. Эта ситуация отслеживается компилятором, который сообщает об UnreachableCodeException (ошибка - недостижимый код). Правильно данная конструкция будет выглядеть так:

    try { ... } catch(UserException ue) { ... } catch(IOException ioe) { ... } catch(Exception e) { ... }

    В этом случае будет выполняться последовательная обработка исключений. И в случае, если не предусмотрена обработка того типа исключения, которое возникло (например, AnotherUserException), будет выполнен блок catch(Exception e){:}

    Если срабатывает один из блоков catch, то остальные блоки в данной конструкции try-catch выполняться не будут.

    Пустой оператор

    Точка с запятой (;) является пустым оператором. Данная конструкция вполне применима там, где не предполагается выполнение никаких действий. Преждевременное завершение пустого оператора невозможно.

    Реализация интерфейса Runnable

    Описанный выше способ создания потоков как объектов класса Thread или унаследованных от него классов кажется достаточнао естественным. Однако этот способ не единственный. Если вам нужно создать только один поток, работающую одновременно с кодом аплета, проще выбрать второй способ с использованием интерфейса Runnable.
    Идея заключается в том, что основной класс аплета, который является дочерним по отношению к классу Applet, дополнительно реализует интерфейс Runnable, как это показано ниже:
    public class MultiTask extends Applet implements Runnable { Thread m_MultiTask = null; . . . public void run() { . . . }
    public void start() { if (m_MultiTask == null) { m_MultiTask = new Thread(this); m_MultiTask.start(); } }
    public void stop() { if (m_MultiTask != null) { m_MultiTask.stop(); m_MultiTask = null; } } }
    Внутри класса необходимо определить метод run, который будет выполняться в рамках отдельного потока. При этом можно считать, что код аплета и код метода run работают одновременно как разные потоки.
    Для создания потока используется оператор new. Поток создается как объект класса Thread, причем конструктору передается ссылка на класс аплета:
    m_MultiTask = new Thread(this);
    При этом, когда поток запустится, управление получит метод run, определенный в классе аплета.
    Как запустить поток?
    Запуск выполняется, как и раньше, методом start. Обычно поток запускается из метода start аплета, когда пользователь отображает страницу сервера Web, содержащую аплет. Остановка потока выполняется методом stop.
    Назад Вперед

    Реализация интерфейса RunnableРеализация интерфейса Runnable Контакты
    О компании
    Новости
    Вакансии
    Правовые аспекты
    Условия использования
    Торговые марки

    Copyright 1994-2005 Sun Microsystems, Inc.
    Реализация интерфейса Runnable
    printmenus();
    Реализация интерфейса RunnableПрограммные продукты
    Рабочие станции и тонкие клиенты
    Серверы
    Системы хранения данных
    Посмотреть все Реализация интерфейса Runnable»


    Реализация интерфейса RunnableSolaris 10
    Java 2 Standard Edition
    Developer Tools
    Top Downloads
    New Downloads
    Патчи и обновления
    Посмотреть все Реализация интерфейса Runnable»


    Реализация интерфейса RunnableКаталог решений
    Истории успеха

    Реализация многопоточности в Java

    Назад Вперед
    должны воспользоваться классом java.lang.Thread. В этом классе определены все методы, необходимые для создания потоков, управления их состоянием и синхронизации.
    Как пользоваться классом Thread?
    Есть две возможности.
  • Во-первых, вы можете создать свой дочерний класс на базе класса Thread. При этом вы должны переопределить метод run. Ваша реализация этого метода будет работать в рамках отдельного потока.

  • Во-вторых, ваш класс может реализовать интерфейс Runnable. При этом в рамках вашего класса необходимо определить метод run, который будет работать как отдельный поток.

  • Второй способ особенно удобен в тех случаях, когда ваш класс должен быть унаследован от какого-либо другого класса (например, от класса Applet) и при этом вам нужна многопоточность. Так как в языке программирования Java нет множественного наследования, невозможно создать класс, для которого в качестве родительского будут выступать классы Applet и Thread. В этом случае реализация интерфейса Runnable является единственным способом решения задачи.

    Синхронизация методов

    Возможность синхронизации как бы встроена в каждый объект, создаваемый приложением Java. Для этого объекты снабжаются защелками, которые могут быть использованы для блокировки потоков, обращающихся к этим объектам.
    Чтобы воспользоваться защелками, вы можете объявить соответствующий метод как synchronized, сделав его синхронизированным:
    public synchronized void decrement() { . . . }
    При вызове синхронизированного метода соответствующий ему объект (в котором он определен) блокируется для использования другими синхронизированными методами. В результате предотвращается одновременная запись двумя методами значений в область памяти, принадлежащую данному объекту.
    Использование синхронизированных методов - достаточно простой способ синхронизации потоков, обращающихся к общим критическим ресурсам, наподобие описанного выше банковского счета.
    Заметим, что не обязательно синхронизовать весь метод - можно выполнить синхронизацию только критичного фрагмента кода.
    . . . synchronized(Account) { if(Account.check(3000000)) Account.decrement(3000000); } . . .
    Здесь синхронизация выполняется для объекта Account.

    Синхронизация потоков

    Назад Вперед
    Многопоточный режим работы открывает новые возможности для программистов, однако за эти возможности приходится расплачиваться усложнением процесса проектирования приложения и отладки. Основная трудность, с которой сталкиваются программисты, никогда не создававшие ранее многопоточные приложения, это синхронизация одновременно работающих потоков.
    Для чего и когда она нужна?
    Однопоточная программа, такая, например, как программа MS-DOS, при запуске получает в монопольное распоряжение все ресурсы компьютера. Так как в однопоточной системе существует только один процесс, он использует эти ресурсы в той последовательности, которая соответствует логике работы программы. Процессы и потоки, работающие одновременно в многопоточной системе, могут пытаться обращаться одновременно к одним и тем же ресурсам, что может привести к неправильной работе приложений.
    Поясним это на простом примере.
    Пусть мы создаем программу, выполняющую операции с банковским счетом. Операция снятия некоторой суммы денег со счета может происходить в следующей последовательности:
  • на первом шаге проверяется общая сумма денег, которая хранится на счету;

  • если общая сумма равна или превышает размер снимаемой суммы денег, общая сумма уменьшается на необходимую величину;

  • значение остатка записывается на текущий счет.

  • Если операция уменьшения текущего счета выполняется в однопоточной системе, то никаких проблем не возникнет. Однако представим себе, что два процесса пытаются одновременно выполнить только что описанную операцию с одним и тем же счетом. Пусть при этом на счету находится 5 млн. долларов, а оба процесса пытаются снять с него по 3 млн. долларов.
    Допустим, события разворачиваются следующим образом:
  • первый процесс проверяет состояние текущего счета и убеждается, что на нем хранится 5 млн. долларов;

  • второй процесс проверяет состояние текущего счета и также убеждается, что на нем хранится 5 млн. долларов;

  • первый процесс уменьшает счет на 3 млн. долларов и записывает остаток (2 млн. долларов) на текущий счет;


  • Создание дочернего класса на базе класса Thread

    Рассмотрим первый способ реализации многопоточности, основанный на наследовании от класса Thread. При использовании этого способа вы определяете для потока отдельный класс, например, так:
    class DrawRectangles extends Thread { . . . public void run() { . . . } }
    Здесь определен класс DrawRectangles, который является дочерним по отношению к классу Thread.
    Обратите внимание на метод run. Создавая свой класс на базе класса Thread, вы должны всегда определять этот метод, который и будет выполняться в рамках отдельного потока.
    Заметим, что метод run не вызывается напрямую никакими другими методами. Он получает управление при запуске потока методом start.
    Как это происходит?
    Рассмотрим процедуру запуска потока на примере некоторого класса DrawRectangles.
    Вначале ваше приложение должно создать объект класса Thread:
    public class MultiTask2 extends Applet { Thread m_DrawRectThread = null; . . . public void start() { if (m_DrawRectThread == null) { m_DrawRectThread = new DrawRectangles(this); m_DrawRectThread.start(); } } }
    Создание объекта выполняется оператором new в методе start, который получает управление, когда пользователь открывает документ HTML с аплетом. Сразу после создания поток запускается на выполнение, для чего вызывается метод start.
    Что касается метода run, то если поток используется для выполнения какой либо периодической работы, то этот метод содержит внутри себя бесконечный цикл. Когда цикл завершается и метод run возвращает управление, поток прекращает свою работу нормальным, не аварийным образом. Для аварийного завершения потока можно использовать метод interrupt.
    Остановка работающего потока выполняется методом stop. Обычно остановка всех работающих потоков, созданных аплетом, выполняется методом stop класса аплета:
    public void stop() { if (m_DrawRectThread != null) { m_DrawRectThread.stop(); m_DrawRectThread = null; } }
    Напомним, что этот метод вызывается, когда пользователь покидает страницу сервера Web, содержащую аплет.

    Создание пользовательских классов исключений

    Как уже отмечалось, допускается создание собственных классов исключений. Для этого достаточно создать свой класс, унаследовав его от любого наследника java.lang.Throwable (или от самого Throwable).
    Пример:
    public class UserException extends Exception { public UserException() { super(); } public UserException(String descry) { super(descry); } }
    Соответственно, данное исключение будет создаваться следующим образом:
    throw new UserException( "Дополнительное описание");

    Управление циклами

    В языке Java имеется три основных конструкции управления циклами:
  • цикл while;
  • цикл do;
  • цикл for.


  • Управление ходом программы

    Управление потоком вычислений является фундаментальной основой всего языка программирования. В данной лекции будут рассмотрены основные языковые конструкции и способы их применения.
    Синтаксис выражений весьма схож с синтаксисом языка С, что облегчает его понимание для программистов, знакомых с этим языком, и вместе с тем имеется ряд отличий, которые будут рассмотрены позднее и на которые следует обратить внимание.
    Порядок выполнения программы определяется операторами. Операторы могут содержать другие операторы или выражения.

    Временная приостановка и возобновление работы

    Методы suspend и resume позволяют, соответственно, временно приостанавливать и возобновлять работу потока.
    В следующем фрагменте кода поток m_Rectangles приостанавливает свою работу, когда курсор мыши оказывается над окном аплета:
    public boolean mouseEnter(Event evt, int x, int y) { if (m_Rectangles != null) { m_Rectangles.suspend(); } return true; }
    Работа потока возобновляется, когда курсор мыши покидает окно аплета:
    public boolean mouseExit(Event evt, int x, int y) { if (m_Rectangles != null) { m_Rectangles.resume(); } return true; }

    В данной лекции рассмотрены основные

    В данной лекции рассмотрены основные языковые конструкции.
    Для организации циклов в Java предназначены три основных конструкции: while, do, for. Для изменения порядка выполнения операторов применяются continue и break (с меткой или без). Также существуют два оператора ветвления: if и switch.
    Важной темой является обработка ошибок, поскольку без нее не обходится ни одна программа, ведь причиной сбоев может служить не только ошибка программиста, но и внешние события, например, разрыв сетевого соединения. Основной конструкцией обработки исключительных ситуаций является try-catch-finally. Для явной инициализации исключительной ситуации служит ключевое слово throw.
    Ошибки делятся на проверяемые и непроверяемые. Чтобы повысить надежность программы, компилятор требует обработки исключений, классы которых наследуются от Exception, кроме классов-наследников RuntimeException. Предполагается, что такие ошибки могут возникать не столько по ошибке разработчика, сколько по внешним неконтролируемым причинам.
    Классы, унаследованные от RuntimeException, описывают программные сбои. Ожидается, что программист сведет вероятность таких ошибок к минимуму, а потому, чтобы не загромождать код, они являются непроверяемыми, компилятор оставляет обработку на усмотрение разработчика. Ошибки-наследники Error свидетельствуют о фатальных сбоях, поэтому их также необязательно обрабатывать.
    Методы, код которых может порождать проверяемые исключения, должны либо сами их обрабатывать, либо в заголовке метода должно быть указано ключевое слово throws с перечислением необрабатываемых проверяемых исключений. На непроверяемые ошибки это правило не распространяется.
    Переопределенный (overridden) метод не может расширять список возможных исключений исходного метода.

    

        Бизнес: Предпринимательство - Малый бизнес - Управление