    Глава 1 

    Язык Java 

    Java — это новый объектно-ориентированный язык программирования, созданный 
с целью облегчить и ускорить процесс программирования для Internet. 
Несомненно, перед покупкой этой книги вам приходил в голову вопрос: "А нужно 
ли мне изучать еще один язык программирования?" Программисты с сединой на 
висках помнят время, когда программы писались на языке Паскаль. Затем наступил 
черед языка С, совсем недавно осваивали C++. Кроме того, возможно, приходилось 
использовать один или несколько из дюжины интерпретируемых или узкоспециальных 
языков, таких как Perl, Shell scripts, Smalltalk и т.д. 
    Применение каждого из этих языков в различных средах имеет свои 
особенности. Все языки оценивается с точки зрения использования в такой новой 
области программирования, как Internet. По той или иной причине каждый из них 
обладает каким-нибудь недостатком. Одной из главных проблем Internet является 
то, как обезопасить систему клиента от вирусов и других атакующих процессов, а 
информационные серверы от атак со стороны систем клиентов, которых они обслуживают. 
    Несколько слов о языке С. Это чрезвычайно мощный язык, предоставляющий 
прямой доступ к вызовам операционной системы низкого уровня. Многие 
компиляторы позволяют включать в файлы ассемблерный код. К сожалению, мощь С 
слишком велика для программирования в Internet. Его очень легко использовать 
для атаки на систему сервера или клиента, так как в Internet нет средств, 
обеспечивающих безопасный запуск программы, написанной на языке С. Кроме того, 
программы на С должны быть заново откомпилированы или даже переписаны при 
использовании на других платформах. Но многообразие платформ — это 
отличительная черта Internet. Написать же программу на языке С, которая 
одинаково успешно запускалась бы на компьютере Macintosh, в среде UNIX или 
среде Windows, почти невозможно. 
    Позаимствовав многие черты и синтаксис у других языков, Java стал лучшим 
на сегодняшний день. Он был создан одновременно и мощным, и безопасным. Будучи 
открытым и "элегантным", он к тому же и платформно-независим. 
    Большую часть своего синтаксиса Java позаимствовал у языков С и C++, самых 
популярных языков разработки приложений для персональных компьютеров.   
Поэтому  программистам,  использующим  С  и  C++,  очень  легко 

    11 

освоить язык Java, причем процесс обучения будет очень плавным. Из языка 
Smalltalk позаимствованы концепции собирания "мусора" и объемной модели 
объектов, из языка Objective-C — концепция интерфейса, которая очень важна
для осуществления моста Java/СОМ. Таким образом, Java — это безопасный, мощный, 
легкий для восприятия, платформно-независимый объектно-ориентированный язык, в 
наибольшей степени подходящий для программирования в системе Internet. 
    Поскольку данная книга написана в предположении, что вы знакомы с 
объектно-ориентированным программированием на C++, в этой главе показано, как 
такое программирование осуществляется на языке Java. Детальное описание 
синтаксиса Java остается за рамками настоящего издания — его вы можете найти в 
книге "Learn Java now", текст которой включен в пакет Visual J++. Особое 
внимание в главе уделяется описанию общих черт и отличий Java от C++, а также 
проведению аналогий с другими объектно-ориентированными языками программирования. 

    Основы Java 

    Когда речь идет о Java, мы можем подразумевать две различные вещи. 
Во-первых, это язык Java, на котором пишут и компилируют классы, во-вторых — 
виртуальная машина Java (Java virtual machine). Для того чтобы Java-программы 
были действительно платформно-независимыми, компиляция всех Java-программ 
выполняется в единой архитектуре. Когда программу Java компилируют на 
платформе Windows/Intel x86, получают такой же выходной модуль, как и при 
компиляции на компьютере Macintosh и в среде UNIX. Компилятор создает код не 
для собственной, а для абстрактной платформы, называемой виртуальной машиной 
Java (BMJ или просто ВМ). Когда запускают Java-программу, на самом деле 
запускают ее в экземпляре ВМ, зачастую реализованной в качестве 
интерпретатора. Фирмы Microsoft и Macintosh создали версии виртуальной машины 
Java, которые в будущем будут интегрированы в соответствующие операционные системы. 
    Итак, мы можем запустить любую откомпилированную Java-программу на любой 
платформе Windows, которая включает в себя виртуальную машину Java фирмы 
Microsoft. Эта фирма запланировала включение ВМ в каждую операционную систему 
Windows в ближайшем будущем. Это означает, что Java будет не только лучшим 
языком программирования в Internet, но и хорошим языком программирования в Windows. 

    Байт-код и класс-Файлы 

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

    12	Visual 1++: основы программирования 

    Единый файл исходного кода Java компилируется с помощью компилятора 
JV CCompiler, включенного в Visual J++, или компилятора фирмы Sun javac, 
включенного в Java Developers Kit. Компилятор создает класс-файл с расширением 
CLASS для каждого класса из файла исходного кода. В этом класс-файле 
содержится байт-код (byte-code) для виртуальной машины Java, представляющий 
собой ряд бинарных инструкций. Такие класс-файлы можно передавать от системы к 
системе и использовать их в любой системе, в которой есть экземпляр ВМ. 

    Классы 

    Класс служит описанием типа объекта в каждом объектно-ориентированном 
языке. В классе определяются три общих для всех объектов класса понятия: 
    •    Набор данных. Аналогично понятию состояние объекта. В классе 
объявляют некоторое количество (часто нулевое) переменных, которые принадлежат 
объектам данного класса. Например, в классе Point, созданном для представления 
точек на плоскости, можно объявить такие относящиеся к объектам данные, как 
переменная х и переменная у. 
    •    Методы класса. Функции или операции, которые могут выполнять объекты 
класса. Обычно эти методы действуют, сообщая что-то о данных объекта или 
модифицируя их. В частности, в классе Point, созданном для представления точек 
на плоскости, можно объявить метод magnitude(), вычисляющий расстояние от 
точки до начала координат (0, 0). 
    •    Родительские классы. Классы могут быть порождены от других классов. И 
в C++, и в Java это называется наследованием. Понять принцип наследования в 
Java и то, как объявляется наследование, необходимо до того, как начинать 
разбираться с другими концепциями классов. 

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

    Как уже говорилось, Java-класс можно объявить наследником какого-либо 
другого класса: 

    class Bar { 
         // Определения данных и методов. 
     } 

    class Foo extends Bar { 
         // Определения данных и методов. 
    } 

    Необходимо отметить, что в классе Foo содержатся все данные и методы, 
определенные в классе Ваг, и еще некоторые собственные декларации. Рассмотрим 
более конкретный случай на примере класса Door, используемого для описания 
дверей. Дверь может быть открытой или закрытой, 

    Глава 1. Язык Java	13 

поэтому наряду с методами ее открытия и закрытия в классе Door следует 
определить булеву переменную, описывающую ее состояние (открыта или же закрыта): 

    class  Door   ( 
       boolean isOpen; 

       void open() { isOpen = true; } 
       void close () { isOpen = false; } 
    } 

    Door является достаточно большим классом. Можно создать экземпляр класса 
для описания парадной двери, дверей черного хода, гаража и т.д. Но что если у 
парадной двери есть замок? Потребуются другие данные, указывающие, заперт 
замок или нет. В этом случае для описания парадной двери можно определить 
класс LockingDoor. И так как последний выполняет то же, что и класс Door, плюс 
что-то еще, может оказаться, что класс Locking-Door легче определить 
наследником класса Door: 

    class LockingDoor extends Door { 
       // Автоматическое получение переменной isOpen и 
       // методов open() и close() из класса Door. 

       boolean isLocked; 

      void lock() { isLocked = true; } 
      void unlock) { isLocked = false; } 
    } 

    Можно продолжить цепочку порожденных классов, объявив в качестве 
LockingDoor класс LockingAlarmedDoor, сопровождающийся описанием звонка. Далее 
можно определить класс LockingAlarmedSecretDoor, являющийся классом 
LockingAlarmedDoor, который может сам показывать и скрывать (делать доступной 
и недоступной) описываемую им дверь (то есть, у которого есть методы hideQ и unhide(). 
    По крайней мере одно достоинство порожденных друг от друга классов 
совершенно очевидно, если исходить из этого примера: возможность многократного 
использования кода. Написанный для исходного класса Door код (методы ореп() и 
close()) не надо переписывать снова для порожденных классов LockingDoor, 
LockingAlarmedDoor, LockingAlarmedSecretDoor и прочих, что значительно 
экономит время. Для тех, кто не знаком с объектно-ориентированным 
программированием, в последующих параграфах данной главы будут описаны другие 
возможности применения порожденных классов. 
    В отличие от C++ в Java поддерживается только одиночное наследование. В 
C++ можно объявить класс, порожденный от нескольких родительских классов, 
который будет обладать всеми способностями и данными каждого из "родителей". 
Это делает программирование довольно "элегантным", но зачастую приводит к 
путанице в исходных кодах. Создатели языка Java решили, что множественное 
наследование, подобное наследованию в C++, 

    14   Visual 1++-. основы программирования 

    затрудняет понимание программ (многие могли убедиться в этом на 
собственном опыте). Поэтому при объявлении Java-класса вы можете указывать 
только один родительский класс, используя ключевое слово extends, как это было 
сделано в предыдущем примере. Для создания подобия множественного 
наследования, как будет показано далее, используется интерфейс Java. 

    Класс Object 

    Фактически все классы Java (за исключением одного) порождены от класса 
Object. Когда вы определяете класс, не являющийся наследником какого-либо 
иного класса, в этом объявлении неявно присутствует оператор extends Object. 
Следующие два определения класса эквивалентны. Они компилируются в один байт-код: 

    class Point { 
	int x; 
	int у; 
    }
    class Point extends Object ( 
    	int x; 
    	int y; 
    } 

    Какие методы и данные представляет класс Object? Так как в конечном счете 
все классы происходят от него, методы Object наследуются всеми классами Java. 
Набора переменных в классе Object нет; его методы перечислены и описаны в 
представленной ниже таблице. Более подробные объяснения каждого из этих 
методов приводятся в других главах книги по мере возникновения такой потребности. 

    Таблица 1.1. Методы класса Object 

    Метоп класса Object     Описание 

    getClass()	Основной идентификатор типа на этапе выполнения. 
    Идентифицирует класс любого произвольного объекта. 

    hashCode()	При использовании в хеш-таблицах возвращает псевдоуникальное 
значение любого объекта. (Класс Hashtable описан в главе 11.) 

    equals()	Сообщает, равен ли объект другому объекту. Сущность 
этого метода различна для разных классов. Обычно он возвращает true, если 
имеется две ссылки на один объект. Класс String возвращает true и в том 
случае, если символы двух строк совпадают. 

    clone()	Создает новый объект, являющийся точной копией исходного. 

    toString()	Возвращает строку, предназначенную для вывода пользователю. 
Строка описывает тип и состояние объекта.

    Глава 1. Язык Java 15 

    Метоа класса Object	Оппсанпе 

    notify(), notifyAll(), wait() 	Используется для синхронизации 
программных потоков. 
    finalize()	Реализует метод деструктора. 


    Переменные 

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

    class Point { 
    	int x; 
    	int у; 
    } 

    В этом примере определяется класс Point с переменными х и у. Обе они имеют 
тип int, обозначающий 32-битовое (4-байтовое) целое. Это достаточно простой пример. 
    В Java имеется (табл. 1.2) несколько так называемых встроенных типов 
данных, таких как int, позволяющих производить почти все необходимые 
математические операции. Тот, кто знаком с С или C++, узнает эти типы. 

    Таблица 1.2. Типы данных Java 

    Тип данных     Оппсанпе 

    char	16-разрядный символ Unicode. (Unicode — новая универсальная 
кодировка символов, теоретически способная описать символы всех существующих 
языков. Unicode служит внутренним представлением символов в 32-битовой системе Windows.) 

    byte	8-разрядное целое число со знаком. Возможные значения — от 
    -128 до 127. 

    short	16-разрядное целое число со знаком. Возможные значения — от 
    -32768 до 32767. 

    int	32-разрядное целое число со знаком. Возможные значения — от 
    -2147483648 до 2147483647. 

    long	64-разрядное целое число со знаком. Возможные значения находятся в 
интервале между двумя числами, представляющими значение, гораздо больше того 
максимального, которое можно точно отобразить на карманном калькуляторе, т. е. 
в диапазоне от 2**(-63) до 2**(63)(минус единица). 

    boolean	Булево значение. Два возможных значения описываются ключевыми 
словами Javа — true и false. 

    16 Visual J++: основы программирования 

    Тип панных	Описание 

    float 	Число с плавающей запятой в формате IEEE 754. 
    double	Число с плавающей запятой удвоенной точности в формате IEEE 754. 

    Перечисленные типы данных подобны типам данных в С и C++, но между ними 
существуют и определенные различия. Во-первых, длина переменных типа char, 
short, int и long в битах неизменна. С другой стороны, в стандарте ANSI 
числовые типы С могут иметь различную длину в зависимости от, платформы. 
Например, в 16-разрядной операционной системе Windows (Windows 3.x) целый тип 
занимает 16 битов, а в 32-разрядной системе (Windows 95 и Windows NT) — 32 
бита. Это означает, что один и тот же С-код на различных платформах 
компилируется по-разному. Что за наказание следить за этими различиями и 
пытаться учесть их в своем коде! Java избавлен от необходимости таких 
"накладных расходов" на дополнительное сопровождение благодаря определению 
переменных встроенного типа, всегда имеющих одно и то же значение для любой 
реализации ВМ. Во-вторых, в Java отсутствуют указатели. Возможно, в этом 
заключается самое важное его отличие от С и C++. В Java используются 
только ссылки, то есть концепция характерная и для C++ (но не для С). Ниже мы 
рассмотрим принцип применения ссылок и то, как именно они обеспечивают 
большую безопасность и легкость программирования на данном языке по сравнению 
с С и C++. 

    Переменные класса и переменные экземпляра 

    Каждый объект класса Point из предыдущего примера имеет по две хранящиеся 
в нем 32-разрядные переменные: х и у. Каждый экземпляр класса Point содержит 
собственные копии этих переменных. Иными словами, двум разным объектам класса 
Point в памяти отведены две различных области пространства, по 32 бита каждая. 
Такие копии называются переменными экземпляра. 
    Вы можете объявить переменные класса, которые будут общими для всех его 
объектов. При объявлений каждой такой переменной используется ключевое слово 
static, например: 

    class Point2 { 
	int x; 
	int у; 
    	static int x_origin = 0; 
	static int у origin = 0; 
    }

    Глава 1. Язык Java      	17 

    У всех объектов класса Point2 помимо переменных х и у теперь имеются общие 
целые переменные, названные x_origin и y_origin. Это значит, что если один из 
объектов класса Point2 изменит значение x_origin, то это значение изменится 
для всех объектов данного класса. С другой стороны, если в каком-либо объекте 
изменится значение х, то в других объектах оно останется прежним. Значение х — 
это переменная экземпляра, а значение x_origin — статическая переменная класса. 

    Зашита переменных и спецификаторы доступа 

    Если рассматривать объект как черный ящик, станет очевидным, что некоторые 
из его переменных доступны для проверки и изменения другими объектами. 
Возможно, вы не хотите, чтобы переменные объекта можно было изменять таким 
образом. Рассмотрим класс LockingDoor из приводимого ранее примера. У всех 
объектов данного класса имеется булева переменная isLocked. Нежелательно, 
чтобы код другого класса мог произвольным образом изменять эту переменную. Это 
было бы аналогично хранению ключа от парадной двери снаружи! Вместо этого 
требуется, чтобы внешние объекты использовали методы lock() и unlock(). Объекты 
класса LockingDoor полностью контролируют значение переменной isLocked 
методами lock() и unlock(). Как в черном ящике, нежелательно, чтобы переменная 
isLocked подвергалась воздействию извне; необходимо, чтобы она была 
инкапсулирована внутри объекта. 
    В Java существуют три различных спецификатора, предназначенных для 
управления доступом к переменным. Ключевое слово public устанавливает, что 
переменная доступна для всех внешних кодов и может быть опрошена или изменена 
любым из них. Переменные х и у класса Point можно сделать доступными, 
используя ключевое слово public: 

    class Point { 
    	public int x; 
	public int y; 
    } 

    Противоположным public является ключевое слово private. Оно показывает, 
что никакой внешний код не может опрашивать или модифицировать переменную. 
Этот спецификатор доступа используется для управления доступом к переменной. 
Переменная isLocked класса LockingDoor служит удачным примером переменной 
private. Вместо того чтобы сделать переменную доступной для любого другого 
кода, вы можете установить тип доступа к ней как к переменной типа private: 

    class LockingDoor extends Door { 
	private boolean isLocked; 
    	void lock() { isLocked = true; } 
	void unlock() { isLocked = false; } 
    }

    18	Visual J++: основы программирования 

    Теперь данная переменная будет доступна методам lock() и unlock() своего 
класса, но не доступна объектам других классов. Единственный способ, которым 
внешний код может модифицировать эти переменные, — использовать методы lock() и 
unlock(). Представленный ниже образец кода, который не компилируется, 
показывает, как нельзя осуществить доступ к переменной private даже из 
порожденного класса. 

    class PasswordLockingDoor extends LockingDoor { 
	private String m_strPassword; 
    	void unlockWithPassword (String strPossiblePassword) { 
	    if(m_strPassword == strPossiblePassword) 
		    isLocked = false; // Неверно! К закрытой переменной isLocked нет доступа. 
      }	
    }

    Хотя класс PasswordLockingDoor порожден от класса LockingDoor, oн не имеет 
доступа к унаследованной переменной isLocked, поскольку она объявлена как private. 
    Public и private — это две крайности. Первый спецификатор разрешает 
опрашивать и модифицировать переменную всем. Второй запрещает доступ к 
переменной всем методам, за исключением методов класса. На самом деле ссылка 
на эту переменную со стороны любого другого класса, включая классы, 
порожденные от LockingDoor, приведет к ошибке (как показано на примере 
PasswordLockingDoor). Рассмотрим булеву переменную isOpen класса Door, от 
которого порожден класс LockingDoor. Нам необходимо, чтобы эта переменная была 
доступна только классу LockingDoor. Другими словами, не хотелось бы разрешать 
кому-нибудь открывать запертую дверь, предварительно не отперев ее. А этого 
можно было бы достигнуть, объявив isOpen public-переменной класса Door: 
достаточно было бы задать переменной isOpen объекта LockingDoor значение true. 
Для разрешения доступа любого порожденного класаа к этой переменной ее 
необходимо объявить как protected: 

    public class Door { 
	    protected boolean isOpen; 
	    // Может использоваться только этим классом и его 
	    // порожденными от него классами, 
	    void open() { 
	    	isOpen = true; 
	    } 
	    void close() { 
		isOpen = false; 
	    } 
    }

    public class LockingDoor extends Door {
	private isLocked; 

    Глава 1. Язык Java	19 

      void lock() { 
   	  // Может только запирать закрытые двери. 
	  if(isOpen) {return;} 
         // Доступ к isOpen разрешен. 
	   isLocked = true 
      } 
      void unlock () { 
          // Может только отпирать запертые двери.. 
          isLocked = false; 
	} 
    }

    Метод lock() класса LockingDoor имеет доступ к унаследованной переменной 
isOpen, поскольку последняя в родительском классе (Door) была объявлена как 
protected. Внешний класс, не порожденный от класса Door, не может иметь 
доступа к этой переменной. По умолчанию уровнем доступа, если не определено 
иначе, является private to package. Ниже такой тип доступа будет подробно 
охарактеризован при описании пакетов Java. Спецификаторы типа доступа 
перечислены в табл. 1.3. 

    Таблица 1.3. Спецификаторы доступа 

    Спецификатор типа доступа к переменной     Описание 

    private	Непосредственный доступ имеют только методы того 
    класса, в котором переменная объявлена. 

    protected	Непосредственный доступ имеют только методы пакета, в котором 
объявлена переменная, или классы, порожденные от класса, в котором объявлена переменная. 

    public	Любой метод любого класса имеет доступ к переменной. 

    <default>	Называется также private to package. Доступ имеют 
    только классы того же пакета, что и класс, в котором переменная объявлена. 
    Более подробно о пакетах рассказано ниже. 

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

    20 Visual J++: основы программирования 

    Методы 

    Все выполняемые коды Java располагаются внутри методов его классов. Вне 
классов выполняемых кодов не существует. В этом состоит отличие Java от С и 
C++, где функции можно объявить в глобальной области видимости (scope), но они 
не будут принадлежать каком-либо классу. В Java нет такого не объявленного в 
классе понятия, как функция или вид выполняемого кода. 

    Некоторые замечания по синтаксису Java 

    Java-код подобен коду на С или C++, и это не просто совпадение. Знание 
языка С или C++ очень облегчает программирование на Java. Рассмотрим 
компактную строку С++-кода: 

    for( int ii = 0; (anVals[++ii] « (ii & 0xF0)) > anThreshholds[ii]; ii++) 

    Точно так же она выглядит на Java: 

    for( int ii = 0; (anVals[++ii] « (ii & OxFO)) > anThreshholds[ii]; ii++) 

    Почти все правила приоритета, синтаксис и т.п., определенные в C++, 
характерны и для Java. Обзор синтаксиса C++ выходит за рамки настоящей книги, 
поэтому для читателя, совершенно не знакомого с этим языком, необходимо 
браться за учебник для начинающих по C++ или по Java. 
    Гораздо легче перечислить, по каким признакам Java отличается от C++, чем 
указать их сходство. Во-первых, уже было показано, как в Java определяются 
классы и как осуществляется одиночное наследование. Возможно, в огдределении 
классов заключается самое большое синтаксическое различие между двумя 
языками. Бместо оператора C++ ':' в Java используется ключевое слово extends:

    class LockingDoor extends Door { 
	...
    }


    Спецификаторы доступа к методам в Java 

    Еще одно значительное отличие состоит в том, как в Java объявляются и 
реализуются методы классов. Если в C++, чтобы включить несколько различных 
методов и переменных в один спецификатор доступа, в качестве меток можно 
использовать спецификаторы private, protected или public, то в lava необходимо 
явно определять доступ к каждому методу или переменной. Кpoмe того, в Java не 
разрешается применять методы с необязательным или переменным числом 
параметров. При объявлении метода используются те же спецификаторы доступа, 
что и для переменных: private, protected или public. Применительно к методам 
они ведут себя так же, как и применительно к переменным. Private-метод можно 
вызвать только в коде его класса, protected-метод — в коде этого класса, в 
коде порожденного класса или в коде пакета, к которому данный класс 
принадлежит, а public-метод — в коде любого класса. 

    Глава 1. Язык Java	21 

    В частности, в ранее определенном классе Door имеются два метода: ореп() и 
close(). Их необходимо определить как public, подразумевая, что они могут быть 
вызваны в коде любого класса: 

    class Door { 
	    protected boolean isOpen; 
		    public void open() { isOpen = true; 
		    public void close () { 
	    isOpen = false; } 
    }


    КОНСТРУКТОРЫ класса 

    Как и в C++, в Java имеются особые методы, называемые конструкторами 
класса (class constructors). Конструктор выполняется, когда создается объект 
класса, и выступает инициализатором объекта. Это подходящее место для 
инициализации переменных и создания объектов — после его завершения вы можете 
быть уверенны, что объект уже можно использовать. Например: в классах Door и 
LockingDoor имеются переменные, которые надо инициализировать до использования 
объектов Door или LockingDoor. Желательно, чтобы для любой созданной двери 
переменная isOpen имела значение false и значение переменной isLocked ддя 
любой запертой двери также бьшо false: 

    class Door { 
	    protected boolean isOpen; 
	    public Door() { 
		    isOpen = false; 
	    }
	    .......
    }

    class LockingDoor extends Door { 
	private boolean isLocked; 
      public LockingDoor() { 
		isLocked = false; 
	}
	...
    }

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

    22 Visual J++: основы программирования 

    Что происходит, когда конструктор не объявляют? В Java он создается 
неявно. Созданный неявно конструктор имеет тип доступа public и фактически 
ничего не делает. Следующие два кода 

    class Point ( 
	int x; 
	int у; 
    }

    И 

    class Point { 
	int x; 
	int y; 

      public Point() { 
	} 
    ) 

    дают один и тот же результат. Параметры могут быть переданы хонструктору 
класса. Объявляют их в списке параметров конструктора так же, как это делают 
в случае других функций, что аналогично С и C++: 

    class Point { 
	int m_x; 
	int m_y; 
    	public Point (int x, int y) { 
		m_x = x; 
		m_y = y; 
    	) 
    ) 

    В предыдущем примере показано, как можно передать значение инициализатора 
конструктору. Эти значения используются для инициализации внутренних 
переменных класса Point — m_х и m_у. 
    В иерархии класса конструкторы связаны в цепочку. Следовательно, при 
каждом вызове конструктора порожденного класса вызывается и конструктор 
родительского класса. Это можно проиллюстрировать следующим примером. Код 

    class Door { 
	    public Door() { 
		    System.out.println("Door being created!"); 
		} 
    ) 

    class LockingDoor ( 
	    public LockingDoor(} { 
		    System.out.println("LockingDoor being created!"}; 
    	    } 
    } 


    Глава 1. Язык Java	23 

    будет выводить результат по мере создания нового объекта LockingDoor: 

    Door being created! 
    LockingDoor being created! 

    Конструктор родительского класса (Door) вызывается как часть конструктора 
LockingDoor. На самом деле это происходит до того, как запускается конструктор 
класса LockingDoor — вот почему первой печатается строка "Door being created!" 
    Вы можете явно вызвать альтернативный конструктор, используя особое 
ключевое слово this. Как и в C++, ключевое слово this ссылается на экземпляр 
того объекта, чей код выполняется. Конструктор this можно применить для вызова 
альтернативного конструктора: 

    class Point { 
	private int m_x; 
	private int m_y; 

      public Point() { 
  	  this(0, 0); 
	} 

      public Point(int x, int y) { 
  	  m_x = x; 
	  m_y = y; 
	} 
   } 

    Конструктор без параметров автоматически вызывает конструктор с двумя 
параметрами (х и у), установленными в 0. 
    Ключевое слово super предназначено для вызова альтернативной версии 
конструктора родительского класса. По умолчанию конструктор порождающего 
класса без параметров вызывается как часть конструктора класса порожденного. 
Но, как видно из следующего примера, можно использовать альтернативную версию 
родительского класса: 

    class Door { 
	    private boolean isOpen;        
	    public Door() { 
		this (true) ;
	    }
	    public Door( boolean isInitiallyOpen) { 
		    isOpen = isInitiallyOpen; 
    	    } 
   } 

   class LockingDoor extends Door { 
	private boolean isLocked; 


    24 Visual J++: основы программирования 


      public LockingDoor()    { 
          //  Вызов  конструктора  Door  с изначально 
          //  закрытой дверью, 
          super (false) ; 
          isLocked =  false; 

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

    class  LockingDoor extends  Door   { private boolean  isLocked; 
	    public  LockingDoor()    { 
	    isLocked =  false;   // Неверно! Первым нужно вызвать конструктор 
			       	    //  родительского класса, 
	    super(false); 


    Создание и удаление объектов 

    В Java объекты не могут быть объявлены в стеке. Все они создаются явно, 
при помощи оператора new. В нашём примере объявляется переменная Door и ей 
приписывается новый объект Door: 

    Door  d =  new  Door(); 

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

    public void MyMetod() { 
	    Door d = new Door(); // Создан первый объект Door. 
	    d = new Door();     /* Создан второй объект Door. Ссылки на 
					 * первый утеряны, поскольку переменной 
                               * присвоены ссылки на разные объекты Door. 


    Глава 1. Язык Java	25 


				    *	Первый объект Door асинхронно 
				    *	уничтожается. */ 
    }                      /*	Ссылка на второй объект Door потеряна, 
				    *	поскольку переменная d вышла из 
				    *	области действия. Второй объект Door будет 
				    *	асинхронно уничтожен. */ 

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

    class Point { 
	int m_x; 
	int m_y; 
    	public Point() { 
		this(0, 0) ; 
      } 
      public Point(int x, int y) { 
		m_x = x; 
		m_y = y; 
	}

    class MyProgram { 
	    public void static main (String astrArgs[] ) { 
    	        Point origin = new Point();  // Создан Point(0, 0). 
		  Point p = new Point(5, 5);   /* Использован второй 
      				          * конструктор Point. 
       					    * Создан Point(5, 5). */ 
	    }
    }

    Часть виртуальной машины Java, ответственная за удаление объектов, на 
которые больше не ссылаются, называется сборщиком мусора (garbage collector). 
Это отдельный программный поток, который запускается внутри ВМ с целью поиска 
объектов, предназначенных для удаления. Так как сборщик мусора является 
отдельным потоком, сбор мусора — это асинхронная операция. Удаляется объект не 
обязательно в момент потери последней ссылки на его экземпляр. Когда именно 
сборщик мусора приступит к уничтожению таких объектов, не известно. Если в 
программе производятся интенсивные 

    26 Visual J++-. основы программирования 

вычисления, то до их завершения кванты процессорного времени потоку 
сборщика мусора выделяться не будут. Однако существует гарантия, что сборщик 
мусора в конце концов уничтожит предназначенные для удаления объекты. 
Для принудительного вызова сборщика мусора следует использовать метод 
System.gc(). 

    Деструктор 

    Как и в C++, у классов Java может быть деструктор. Деструктор объекта 
вызывается тогда, когда сборщик мусора принимается за его удаление. Так как в 
Java отсутствует такое понятие как функции, выделяющие область памяти, 
потребность в деструкторе возникает, по-видимому, гораздо реже, чем в C++. 
    У всех деструкторов Java одно имя —finalize. Данный метод наследуется из 
класса Object, который служит конечным родительским классом всех классов Java. 
При реализации метода finalize() объявляется код, который запускается 
непосредственно перед тем, как объект полностью удаляется из памяти. 

    Вызов метопов объекта 

    Оператор точка (dot) используется для вызова метода объекта. Происходит 
это таким образом: 

    class Point { 
	    public int m_x; 
	    public int m_y; 
          public String toString() { 
		    return "Point (" + m_x + "," + m_y + ")"; 
          }
    }

    class MyProgram { 
          public static void main(String astrArgs[]) ( 
		Point p = new Point(); 
		p.m_x = 5; 
		p.m_y = 6; 
	      System.out.println(p.toString()); 
	    } 
    } 

    Если откомпилировать и запустить этот код, то на выходе получим следующее: 

    Point   (5,    6) 

    Как видим, один и тот же оператор (точка) используется и для доступа к 
переменным объекта, и для вызова методов объекта. Строка р.m_х записывает в 
переменную m_х объекта р некоторое значение, строка p.toString() вызывает метод 
toString() объекта р. 

    Глава 1. Язык Java        27 


    Статический иницализатор 

    Чтобы инициализировать статическую переменную, нужно создать один или 
несколько кодов статических инициализаторов. Все они объединяются в один 
инициализатор класса. Инициализатор класса запускается одновременно с 
загрузкой класса в ВМ, до создания объекта этого типа. Для обозначения частей 
кода инициализатора следует использовать ключевое слово static: 

    class School { 
	    private static String m_strPrincipal; 
	    private static String m_strSecretary; 

	    static ( 
		    m_strPrincipal = "Ms. Doherty"; 
		    m strSecretary = "Mr. Svenson"; 
	    }

	    private static String m_strFirstGradeTeacherl; 
	    private static String m_strFirstGradeTeacher2; 

	    static { 
		    m_strFirstGradeTeacherl = "Mrs. Danielson", 
		    m_strFirstGradeTeacher2 = "Mr. York"; 
	    }
    }

    В этом коде будет создан инициализатор класса, который присвоит значения 
четырем статическим переменным: m_strPrincipal, m_strSecretary, 
m_strFirstGradeTeacherl, m_strFirstGradeTeacher2. Создается только один 
статический инициализатор, построенный из всех блоков кода статических 
инициализаторов, объявленных в классе.
    Очевидно, что код статического инициализатора не может ссылаться на 
нестатическую переменную. Когда запускается код статического инициализатора, в 
наличии нет ни одного объекта класса. Статический инициализатор запускается 
сразу же после загрузки класса в ВМ, то есть до создания какого-либо объекта. 

    Переменная this 

    В методе Java определена переменная, которая называется this и является 
ссылкой на объект, чей метод вызывается. Переменная this очень похожа на 
указатель this в C++. Ссылкой же она является потому, что указатели в данном 
язке отсутствуют. В прёдлагаёмом далее примере продемонстрировано, как this 
ссылается на объект, чей метод вызывается (используется метод isOrigin()):

    class Point { 
	    private	int m_x; 
	    private	int m_y; 


    28	Visual J++: основы программирования 


	    private static Point m_origin; 

	    static { 
		    m_origin = new Point (0, 0); 
	    } 

	    public Point(int x, int y) { 
		    m_x = x; 
		    m_y = y; 
	    } 

	    public Point getOrigin() { 
		    return m_origin; 
	    } 

	    public boolean isOrigin() { 
	    	return (this == m_origin); 
	    } 
    ) 

    Таким образом, метод isOrigin() из этого примера проверяет, совпадает ли 
объект, чей метод вызывается (объект, на который ссылаются при помощи 
переменной this), с ранее размещенным статическим объектом Point. 

    СТРОКИ 

    Ранее было оговорено, что все объекты Java должны создаваться явно при 
помощи оператора new. В действительности же это не всегда так. Для создания 
двух типов объектов имеется особый синтаксис: строки String и массивы. Сначала 
поговорим о строках. 
    Во-первых, следует отметить, что объекты класса String автоматически 
создаются, когда в коде Java встречается строка символов, заключенных в 
кавычки. Следующий код вполне корректен для Java, так как заключенные в 
кавычки символы на самом деле являются объектами типа String. 

    String s = "This is a string object. "; 
    int len = "You can call my methods! ".length(); 

    Во-вторых, у двух операторов появляется особое значение при их 
использовании с объектами типа String. Оператор '+' производит слияние строк, 
когда таковыми являются и левый, и правый операнды. В Java легко создавать 
строки и работать с ними: 

    String strConcat = "The time" + "is now!"; 

    Если только один из операндов оператора '+' представляет собой строку, то 
другой также преобразуется автоматически в строку, и лишь затем осуществляется 
слияние. Создание читаемых строк в Java происходит значительно легче, чем в С 
или C++. Задав

    int nHours = 5; 
    int nMinutes = 22; 


    Глава 1. Язык Java	29 


    int nSeconds = 45; 
    ...
    String strMessage="The time is" + nHours + ":"+nMinutes+" : "+ nSeconds; 

    получим строку 

    "The   time   is   5:22:45" 

    Встроенные типы данных Java (булев, целочисленный и прочие) автоматически 
преобразуются в строки. В случае объектов такое невозможно. Однако в каждом 
классе реализован метод toStringO, который при необходимости используется для 
преобразования объекта в строку, как это происходит при слиянии объекта со строкой. 
    Метод equals(), наследуемый из класса Object всеми объектами типа
String, повторно реализуется в классе String. Обычно этот метод возвращает 
значение true, если ссылки на два объекта на самом деле относятся к одному и 
тому же объекту. Реализация метода equals() в классе String возвращает значение 
true, если два разных объекта типа String состоят из одинаковых строк 
символов. Следующий код устанавливает значение fEquals равным false: 

    Point p1 = new Point(1, 1); 
    Point p2 = new Point(1, 1); 
    boolean fEquals = p1.equals(p2); 

    А этот код устанавливает значение fEquals равным true, так как обе строки эквивалентны: 

    String strl = "Marvy! "; 
    String str2 = "Mar" + "vy! "; // =="Marvy!" 
    boolean fEquals = strl.equals(str2); 


    Массивы 

    В Java массивы обрабатываются несколько по-иному, нежели в C++. Массив в 
Java — это тип объекта, поэтому все массивы порождены от класса Object. Их 
можно приводить к типу Object, производить над ними те же действия, что и над 
объектами. Это довольно интересно, так как дает определенные "выразительные" 
возможности, которые нелегко реализовать в C++. 
    Начнем с самого простого. Объявление массива происходит так же, как и в 
C++. Ниже объявляется массив из пяти целых элементов: 

    int aArray[5]; 

    Судя по этому коду, здесь, подобно классу String, также существуют методы 
неявного размещения массивов. Это значит, что для создания массивов не 
обязательно использовать оператор new, даже если эти массивы — объекты. 
Конечно, данный оператор можно применить: 

    int  aArray[]   =  new  int[5]; 


    3O	Visual J++: основы программирования 

    Однако в Java имеется несколько способов объявления или создания массивов. 
Первый, основной, в наибольшей степени похож на способ, характерный для С: 

    <type> <var-name>[<length>]; 

    Так размещается массив известного размера и дается ссылка на этот объект 
Array в переменной <var-name>. Полученная переменная <var-пате> — это 
переменная с ссылкой на объект. Типом <var-name> является <type>[]. Так, в 
коде

    int aArray[10]; 

    переменная aArray имеет тип int[]. Переменная aObjects имеет тип Object[] 
в следующем примере: 

    Object aObjects[15]; 

    Вы можете заново присвоить эти переменные альтернативным объектам Array, 
размещаемым явно или неявно: 

    int aArray[5];        // Размещен первый массив целых. 
    ...
    aArray = new int[15];  //	Ссылка на первый массив удалена, 
				    //	он будет уничтожен сборщиком мусора. 
				    //	Размещен второй массив, в aArray записана 
				    //	ссылка на него. 

    В этом состоит существенное отличие Java от C++, где размер массивов 
изменять нельзя. Необходимо помнить, что переменные массива, как и других 
объектов, на самом деле хранят ссылку на объект. Тип этого объекта — <type>[]. 

    Массив объектов 

    Когда мы говорим о размещении массива, то подразумеваем размещение не 
самих объектов, а только массива ссылок на них. Следовательно, приведенный 
ниже код неверен: 

    Point aPoints[5];    // Размещен массив ссылок на Point. 
    aPoints[0].m_x = 10; /* Неверно! Объект Роiпt для 
			       /* Point[0] еще не размещен! 

    Вместо него необходимо использовать код наподобие следующего: 

    Point aPoints [5];    // Размещен массив ссылок на Points. 
				    // Создание, экземпляров Points, на которые 
				    // ссылаемся массив, 
     for(int ii=0; ii<5; ii++)    
	   aPoint[ii] = new Point() 

    aPoints[0].m_x = 10; // Объект Point элемента массива Points[0] 
				    // размещен. 


    Глава 1. Язык Java	31 

    Фактически только массивы встроенных типов могут использоваться без 
применения приведенного выше цикла for. В частности, данный цикл не требуется 
для размещения массива целых, так как элементы массива — это целые числа, а не 
ссылки, которые надо инициализировать. 

    Инициализаиия массивов 

    При создании массива элементов встроенного типа, как и в C++, можно 
указать список инициализирующих значений: 

    int   an[]   =   {5,    7,    9,    10,   5,   2,   1,   8,    13}; 

    Таким образом размещается новый массив из девяти целочисленных элементов 
(код такой же, как и в C++). 

    Многомерные массивы 

    В Java легко объявлять многомерные массивы и работать с ними. Вот способ 
размещения двухмерного массива целых: 

    int  ааn[10][10] ; 

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

    Point aap[10][10] ; 
    for(int ii=0; ii<10; ii++) 
	    for(int jj=0; jj<10; jj++) 
		    aap[ii][jj] = new Point(); 


    Альтернативный синтаксис объявления массивов 

    Рассмотрим еще один синтаксис объявления массивов в Java, который 
отличается от применяемого в C++. Описательный оператор [] может быть 
размещен не после имени переменной, а после указателя типа массива. 
Приведенные ниже два объявления метода класса main() эквивалентны. 
Единственное различие между ними заключается в расположении описательного 
оператора [] в списке параметров. Оба примера компилируются в один байт-код: 

    public static void main(String astrArgs[]) { 

    И 

    public static void main(String[] astrArgs) ( 


    32 Visual J++: основы программирования 

    Точно так же эквивалентны два объявления массива an. Первый: 

	int  an[10];

    и второй:

      int[10]   an; 

    Перегрузка методов 

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

    class  Point	{ 
	    private	int m_x; 
	    private	int m_y; 
	    public Point() { 
		    this(0, 0); 
	   } 
         // Перегруженный конструктор: у него другое количество 
         // и иной тип параметров, чем у объявленного ранее, 
	  public Point (int x, int у) { 
  	    m_x = x; 
	    m у = у; 
	  }
    }

    Таким же образом можно создать перегруженные реализации любого метода 
класса, если для каждой реализации принимается другое количество или иной тип 
параметров. Эта концепция аналогична принятой в C++: перегрузка методов 
означает повторное использование мётода с тем же именем, но с другими 
параметрами. В этом примере мы видим, что используются две альтернативные 
реализации метода distance() класса Point. Версия метода distance(), для которой 
не требуется указывать параметры, вычисляет расстояние от точки до начала 
координат (0, 0), а другая версия — расстояние между двумя точками. 

    class   Point   { 
	    public int m_x; 
	    public int m_y; 
	    public Point() { 
		    m_x = m_y = 0; 
	    } 


    Глава 1. Язык Java	33 


	   // Вычисляется расстояние до начала координат, 
	   public double distance () { 
            return Math.sqrt((ma_x*m_x) + (m_y*m_y)); 
         }
        // Вычисляется расстояние до другой точки, 
	  public double distance(Point p) { 
 		int dx = this.m_x - p.m_x; 
		int dy = this.m_y - p.m_y; 
	      return Math.sqrt((dx*dx) + (dy*dy)); 
	  }
    }

    При вызове того или иного из перегруженных методов distance() класса Point 
BM автоматически определяет, какая версия должна быть использована. При этом 
она исходит из того, какие параметры передаются. Если никакие параметры не 
переданы, надо использовать первую версию метода distance(); если передан один 
объект Point (или объект класса, порожденного от класса Point), то применяется 
вторая версия метода. 

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

    Переопределение метода класса означает предоставление новой реализации 
данного метода, отллчной от реализации, унаследованной от родительского, 
класса. В С++ переопределение метддов достигалось путем объявления метода 
virtual, что означало возможность обеспечить его альтернативную реализацию в 
порожденном классе. В этом смысле все методы Java — виртуальные. Например: 
метод toString(), реализованный в классе Object (конечный родительский класс 
для всех остальных классов Java), может быть повторно реализован или 
переопределен в любом классе. 
    Класс в следующем примере, называемый NamedPoint, порожден от класса 
Point. В этом классе нет public-конструктора, поэтому внешний код не может 
создавать объекты такого типа. В таком случае следует использовать статический 
метод lookupNamedPoint() класса NamedPoint, передавая ему строку, содержащую 
имя точки. Обязанность этого класса — гарантировать, чтобы только уникальные 
имена были связаны с отдельными объектами NamedPoint. Это довольно 
распространенная схема программирования в Java. Здесь ссылки на имеющийся 
экземпляр класса хранятся в классе, поэтому их можно позднее найти. (В этом 
классе для хранения ссылок на имеющиеся точки используется объект Hashtable, 
описание которого приведено в главе 11 настоящей книги. Данный объект 
представляет собой тип устройства хранения, которое поддерживает уникальный 
ключ, ассоциируемый с одним или несколькими объектами.) 

    class NamedPoint extends Point { 
	    private static Hashtable s__hash; 
	    // Private-конструктор — нельзя использовать 
	    // оператор new для создания NamedPoint вне кода 
	    // этого класса. 

    34	Visual J++: основы программирования 


	    private NamedPoint () { 
		    // Вызов конструктора порождающего класса, 
		    super (); 
	    }
	    // Метод поиска ищет имеющиеся NamedPoints. Если название 
	    // не опознано, создается новый объект NamedPoints. Так как 
	    // мы находимся внутри кода класса NamedPoint, то можно 
	    // вызвать private-конструктор, используя оператор new. 
	    // Метод статический, поскольку не ссылается на 
	    // нестатические переменные. 
	    public static NamedPoint lookupNamedPoint(String strName) { 
		    Object objReturn; 
		    // Поиск имени в хеш-таблице 
		    if (null == (objReturn = s_hash.get(strName)) { 
			objReturn = new NamedPoint(); 
			s_hash.put(strName, objReturn); 
		    } 
		    return objReturn; 
	    }
    }


    Абстрактные методы 

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

    abstract class Shape { 
       // Абстрактный метод getArea() реализуется порожденным 
       // классом, 
       public abstract double getArea(); 
	 .....
    }

    Отметим, что класс Shape необходимо объявлять ключевым словом abstract, 
показывающим, что данный класс включает в себя по крайней мере один 
абстрактный (или нереализованный) метод. Это значит, что в действительности вы 
можете создать не экземпляр класса Shape, а только объекты порожденных 
классов, которые должны обеспечить реализацию метода getArea(). 
Проиллюстрируем это с помощью следующего примера: 

    class Rect extends Shape { 
	    public int m x, my, m_width, m height; 


    Глава 1. Язык Java	35 


	    public Rect() { 
		    m_x = m_y = m_width = m_height = 0; 
	    } 
          // Реализация абстрактного метода Shape.getArea() 
          // Требуется, если надо создать объект Rect. 
         public double getArea() { 
		    return (m_width * m_height); 
	   ) 

	    class MyProgram { 
		    public static void main(String astrArgs[]) { 
			    Shape s = new Shape ();  /* Неверно! Нельзя, .создавать 
							    * объект абстрактного класса. 
							    * Компилятор укажет ошибку. 
			    s = new Rect();   /* Класс Rect не является абстрактным 
						    * и порожден от класса Shape. 
						    * Поэтому можно выполнять операцию 
						    *   присваивания с переменной типа Shape.
			    System.out.println("Area of shapeis:"+s.getArea()); 
	    }
     }

    Здесь переменной s типа Shape присвоена ссылка на объект типа Rect. Это 
пример допустимого неявного преобразования, поскольку Rect порожден от класса 
Shape. В конструкции s.getShape() вызывается соответствующий метод, 
определенный в классе Rect. 

    Приведение переменных 

    Как и в C++, приведение происходит, когда ВМ получает указание 
использовать альтернативный тип данной переменной или объекта. Так, в 
большинстве последних примеров, где использовались классы Shape и Rect, тип 
объекта Rect приводится к типу Shape. Это возможно, поскольку класс Rect 
порожден от класса Shape и все объекты Rect также являются объектами Shape. 
    Java в отличие от C++ строго типизирован. Это значит, что все подобные 
операции приведения проверяются во время выполнения или компилятором, или 
ВМ (часто обоими одновременно), гарантируя легальное преобразование. Нелегальным 
считается приведение объекта к несовместимому типу. Нелегальное приведение — попытка 
преобразовать объект Rect в объект типа Parallelogram — показано на примере 
следующего кода. Поскольку Rect порожден непосредственно от класса Shape, 
такое преобразование недопустимо. Оно приведет к выдаче компилятором сообщения 
об ошибке. 

    class Rect extends Shape { 
	    public int m_x, m_y, m_width, m_height; 


    36	Visual J++: основы программирования 


	    public Rect() { 
		    m x = m у = m width = m_height = 0; 
	    }

	    public double getArea() { 
		    return (m width * m height); 
          } 
    }

    class Parallelogram extends Shape { 
	    public Point m_vertexl, m_vertex2, m_vertex3, m_vertex4;
	    public Parallelogram() ( 
		    m_vertexl = new Point 0; 
		    m_vertex2 = new Point(); 
		    m_vertex3 = new Point 0; 
		    m_vertex4 = new Point(); 
          } 
	    public double getAreaO { 
 		    return (<calculate area of parallelogram here>); 
  	    }
    }

    class MyProgram { 
	    public static void main(String astrArgs[]) { 
			Rect r = new Rect 0 ; 
			r = new Parallelogram();  /* Неверно! Нельзя 
				           	       * приводить к типу неродственного класса. */ 
	    }
    }

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

    Shape s = new Rect(); 

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

    Rect r = new Rect(); 
    Shape s = new Rect();	
    r = (Rect)s;

    Ссылка на объект Rect присвоена nepeменной типа Shape. Так как 
переменная s принадлежит классу Shape, то для присвоения объекту r класса 
Rect ссылки на s необходимо произвести явное приведение переменных. При 

    Глава 1. Язык Java	37 

неявном приведении проблем не возникает. Явное приведение может оказаться неверным: 

    Rect r; 
    Shape s = new Parallelogram(); // Parallelogram порожден от Shape, 
    г = (Rect)s; /* Неверно! преобразование прекращается, поскольку 
                  * оно не корректно.*/ 

    Однако это не приведет к ошибке при компиляции. ВМ распознает ошибочное 
преобразование, произойдет немедленная выдача исключения ClassCastException. 
(Исключения и обработка исключений описаны ниже.) 

    Приведение переменных встроенных типов 

    Существуют определенные правила приведения переменных встроенных типов. 
Если во время преобразования не происходит потерь в точности, то 
разрешены неявные преобразования. Можно преобразовывать переменные типа int в 
переменные типа double, поскольку второй более точный, чем первый, и потери 
информации не происходит. 
    Однако обратное приведение, в частности от double к int, необходимо 
осуществлять явно. Это покажет компилятору, что вы знаете о потенциальной 
потере информации — ведь у числа с двойной точностью имеется дробная часть, 
которая будет потеряна во время приведения к целому. 
    Приведение можно осуществлять только между переменными числового типа. 
Нельзя приводить, например, переменные числового типа к переменным булевого 
типа. В отличие от С и C++ в Java булев тип заметно отличается от 
числовых. Здесь не может быть взаимного преобразования. Нельзя приводить и 
переменные типа char к числовому типу. Но если вы следуете правилу явного 
приведения с потерей точности, преобразования возможны между переменными таких 
типов: byte, short, int, float, double. 

    Идентификация типа на этапе выполнения 

    В отличие от некоторых реализаций С++ в Java производится идентификация 
типов переменных во время выполнения. Это позволяет узнать класс объекта по 
ссылке на этот объект. Булев оператор instanceof показывает, является ли 
объект объектом данного типа: 

    Shape s = new Rect(); 
    Shape s2 = new Parallelogram(); 
    if(s instanceof Rect) { // true, объект — Rect. 
    if(s instanceof Shape) { // true, так как Rect порожден от Shape. 


    38 Visual J++: основы программирования 


    if(s2 instanceof Rect) { // false, Parallelogram — не Rect. 

    Как видим, оператор instanceof возвращает значение true, если класс 
объекта совпадает с классом, указанным вторым операндом, или порождается от 
него. Другими словами, оператор возвращает значение true, если объект можно 
привести к типу указанного класса. 

    Интерфейсы 

    Интерфейсы — это концепция, которую Java позаимствовал из языка 
Objectiv-С; C++ она не свойственнa. Имея представление об OLE/COM, вы быстро 
поймете концепцию интерфейса. 
    Интерфейс — это, как правило, список объявлений абстрактных методов. Так 
как их реализации отсутствуют, сам интерфейс никаких действий не выполняет. 
Объявление простого интерфейса Advisable может выглядеть таким образом:

    interface Advisable {
	    public void advise(Object obj); 
    } 

    Интерфейс Java аналогичен виртуальному родительскому классу C++. Это табор 
объявлений методов, без их реализации. В предыдущем примере определен интерфейс 
с двумя методами. 
    Чтобы указать, что данный класс Java реализует все методы определенного 
интерфейса, используется ключевое слово implements: 

    class Advisee implements Advisable { 
	public void advise(0bject obj) { 
	    System.out.println("Advisee advised about " + obj); 
       } 
    } 

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

    interface Advisable ( 
    	public void advise(Object obj); 
    } 
    interface WarmAdvisable extends Advisable { 
	    public void warmAdvise(); 
    }

    Глава 1. Язык Java	39 


    interface Moveable { 
	    public void move(int x, int y) ; 
    } 

    class Thing implements WarmAdvisable, Moveable { 
        private int m_x, m_y;
        public Thing() { 
	    m_x = m_y = 0; 
        } 
        // Методы интерфейса WarmAdvisable. 
        public void advise(Object obj) { 
	    System.out.println("Thing advised about " + ob j ) ; 
        } 
        public void warmAdvise() { 
	    System.out.println("Thing given a warm advise."}; 
        }
        // Методы интерфейса Moveable. 
        public void move(int x, int y) {
	    m_x = x; 
	    m__y = y; 
        }  
     }


    Объект можно преобразовать в тип одного из его интерфейсов следующим образом: 

    class myProgram { 
	    public static void MoveHalfwayTo(int x, int y, Moveable m) { 
             /* m — объект, который реализует интерфейс Moveable, 
              * поэтому он реализует метод move(). */ 
             m.move(x/2, у/2); 
          } 
	    public static void AdviseAboutMove(int x, int y, Advisable a) { 
		/* a — объект, реализующий интерфейс Advisable, 
 	       * поэтому он реализует метод advise(). */ 
             a.advise(new Point(x,у)); 
          } 

          public static void main(String astrArgs[]) { 
              Thing = new Thing(); 
              MoveHalfwayTo(10, 10, thing);  // Приводим thing к Moveable. 
              AdviseAboutMove(5, 5, thing);  // Приводим thing к Advisable. } } 


    40 Visual J++: основы программирования 


    Как видим, объявление интерфейса очень похоже на объявление класса. 
Интерфейс может порождаться от другого интерфейса, как, например, класс 
WarmAdvisable порождается от класса Advisable. Это означает, что в интерфейсе 
WarmAdvisable объявлено все то же, что и в интерфейсе Advisable, плюс что-то 
еще. Любой класс, который реализует интерфейс WarmAdvisable, реализует и 
интерфейс Advisable, что и показано в предыдущем примере. Объект Thing может 
быть приведен к типу Advisable, хотя класс Thing объявлен реализующим 
интерфейс WarmAdviseable. 
    В ВМ фирмы Microsoft интерфейсы используются для перехода от объектов Java 
к СОМ-объектам. Применяя компилятор Java/COM, можно создавать интерфейсы Java, 
которые являются аналогами интерфейсов СОМ. Приводя объекты СОМ к типу таких 
интерфейсов, можно вызвать методы СОМ-интерфейса в Java! Другими словами, 
таким образом осуществляется связь между объектами СОМ и Java. C помощью 
соответствующих интерфейсов объекты Java могут управлять таблицей Ехсеl или 
документом Word, которые реализованы с использованием СОМ-интерфейсов. И 
наоборот, с помощью соответствующих СОМ-интерфейсов можно управлять объектами 
Java из кода Visual Basic или C++. Все эти соединения "прозрачны"; вам никогда 
не придется иметь дело с ВМ или коммуникационными системами. 
    Уже из одного примера связи Java/COM видно, как важна концепция 
интерфейса. В главе 6 подробно рассказывается о методике связей Java/COM. 
Кроме того исдользование нтерфейсов Java в ваших программах позволит 
сделать код более простым и легким для понимания. 

    Пакеты 

    В Java все файлы с расширением CLASS определенным образом организованы в 
тесно связанные группы классов, или пакеты (packages). Ключевое слово package 
указывает, что класс принадлежит к определенному пакету. Это означает 
размещение класс-файла в локальной системе файлов, а также привилегированный 
достуа к переменным и мтодам класса из того пакета, к которому класс принадлежит. 
    Для объявления класса частью пакета применяется ключевое слово package. 

    package com.osborne.VJ-Handbook; 
    class MyDemoClass { 
    ...
    }

    Здесь класс MyDemoClass объявлен частью пакета, названного 
com.osborne.VJ-Handbook. Какие преимущества можно получить от использования 
пакетов в Java, будет рассказано далее. 
    Имена пакетов всегда записываются в следующем виде: 
<word>[.<word>\[.<word>\. В имени пакета не должно быть пробелов или символов, 
которые не используются в именах переменных. 

    Глава 1. Язык Java	41 


    Расположение Файлов с расширением CLASS 

    Имя пакета класса определяет, где должен находиться класс-файл. Каждое 
слово в имени пакета интерпретируется как имя подкаталога. Таким образом, в 
предыдущем примере указано, что файл MyDemoClass.class находится в локальной 
системе файлов в подкаталоге с именем com/osborne/VJ-Handbook. Если класс-файл 
там отсутствует, то ВМ не может загрузить его. 
    Какой каталог является корневым для подкаталога com/osborne/VJ-Hand-book? 
Это может быть любой каталог, указанный в переменной среды CLASSPATH. Здесь 
представлен ряд корневых каталогов, в которых следует искать класс-файлы. Если 
загруженный класс принадлежит указанному пакету, то в корневых каталогах 
ищутся подкаталоги с соответствующими именами. Класс-файл должен находиться в 
подкаталоге с именем, определяемым именем пакета, а сам подкаталог должен быть 
расположен в одном из корневых каталогов, определяемых переменной CLASSPATH. 
Значение CLASSPATH может выглядеть следующим образом: 

    set CLASSPATH=" .;С:\Java\Classes;С:\Windows\java\classes" 

    Здесь указано, что существуют три корневых каталога CLASSPATH: текущий 
(указан как '.'), C:\Java\Classes и C:\Windows\Java\Classes. 
    При доступе к классам через Internet (в частности, при загрузке апплета с 
WWW-страницы) имя пакета используется в качестве основного имени родственного 
URL. Например, чтобы просмотреть WWW-страницу со встроенным апплетом, 
необходимо воспользоваться URL http://www.my.do-main.com/java/demo/index.html. 
Этот апплет ссылается на класс MyDemo-Class. Чтобы загрузить файл 
MyDemoClass.class, Web-броузер автоматически создаст URL 
http://www.my.domain.com/java/demo/com/osborne/VJ-Hand-book/MyDemoClass.class 
и попытается загрузить этот файл. 

    Привилегии доступа к частям обшего пакета 

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

    package com.osborne.VJ-Handbook; 
    class Stuff { 
	    boolean fFlag; // He указан модификатор доступа. 
    } 

    Переменной fFlag даны привилегии, называемые только для пакета (private to 
package). Прямой доступ к переменной fFlag объекта Stuff могут иметь только 
функции, являющиеся частью классов пакета com.osborne.VJ-Handbook. 

    42	Visual J++: основы программирования 

    Понятие "привилегии доступа по умолчанию" сходно понятию "дружественные 
функции" (friend) в C++. Привилегии доступа позволяют кодам "неродственных 
классов иметь доступ к переменным и методам типа private. Аналогичным образом 
указатель friend в C++ позволяет коду, не связанному с иерархией классов, 
иметь доступ к переменным и методам типа protected и private. 

    Вид Java-программ 

    Весь код Java-программ додержится в клaccaх Java. Выполняемые 
Java-программы — апплеты, сервлеты и им подобные модули состоят из одного 
или нескольких классов, располагающихся в соответствующих класс-файлах. 
    Как же все-таки написать Java-программу? Лучше всего начать с примера и 
разобрать программу HelloWorld. Для тех же читателей, кому приходилось слишком 
часто сталкиваться с программой HelloWorld, здесь представлена совершенно 
другая программа — HelloUniverse. Она состоит из одного файла исходного 
текста, названного HelloUniverse.java: 

    public class HelloUniverse { 
	    public static void main(String astrArgs[]) { 
		System.out.println("Hello, Universe! "); 
	    } 
    ) 

    В этом файле определен лишь один класс, называемый HelloUniverse. Однако 
выполняемые Java-программы могут состоять и из нескольких классов.
    Класс HelloUniverse реализует один метод — main(). Единственное необходимое 
условие выполнения Java-программы состоит в том, что метод main() должен быть 
определен так же, как в данном примере. Это точка входа в программу. При ее 
запуске вызывается именно этот метод. Если рассматривать Java-программу как 
черный ящик, то единственной его внешней характеристикой будет метод main(), 
как в примере выше. 
    Единственная строка кода выводит текст "Hello, Universe!" в стандартный 
выходной поток Java-программы. Это осуществляется с помощью класса System, 
включенного в базовый комплект Java. Переменная out — статическая переменная 
класса System, объявленная как private,— является выходным потоком данных, 
связанным со стандартным потоком вывода. (У WWW-anплетов выходной поток 
данных System.out связан с Java-консолью Web-броузера, если таковая имеется.) 

    Глава 1. Язык Java	43 

