Лекция №3.
Переменные.
Для объявления переменной используется следующее выражение:
[модификаторы] тип имя_переменной;
В данном случае тип — это либо один из простых типов (int, char, Boolean и т. д.), либо имя класса.
Простые типы используются так же, как и в языке C. После объявления переменной в неё можно записывать различные значения, включать её в состав выражения и т. д.
На первый взгляд кажется, что имена классов одинаково используются в качестве типов переменных как в Java, так и в C. Однако сходство это чисто внешнее.
Предположим, что в программе на языке C определён класс с именем ClassC. Приведённое ниже выражение создает экземпляр класса ClassC и размещает его в переменной с именем objC.
Посредством objC мы можем обращаться к переменным и функциям-членам класса ClassC.
Рассмотрим подобную ситуацию в программе на языке Java. Предположим, что в ней определён класс с именем ClassJava и что в тексте программы присутствует такое выражение
ClassJava objJava;
В отличие от языка C, экземпляр класса не создаётся. Данное выражение объявляет переменную, которая может хранить ссылку на экземпляр класса ClassJava.
В языке Java отсутствуют указатели. И это не случайно, ведь , Java-апплеты могут перемещаться по глобальной сети, поэтому подобная программа должна быть лишена любой возможности непосредственно обращаться к ресурсам локального компьютера. При наличии указателей добиться выполнения запрета на доступ к ресурсам практически невозможно, ведь указателю можно присвоить любое значение, а затем использовать его для обращения к произвольной области памяти.
С другой стороны, работать с объектами без указателей крайне сложно, поэтому в Java был реализован механизм ссылок. Упрощённо ссылку можно представить себе как указатель, который может указывать лишь на существующий объект определённого типа.
Для создания объекта и связывания с этим объектом переменной определенного типа служит оператор new. Вернёмся к рассмотренному выше примеру. После того как в программе определена переменная objJava типа ClassJava, мы можем включить в программу следующее выражение:
objJava = new ClassJava ();
Очевидно, что ClassJava () — это один из конструкторов данного класса. Он создаёт экземпляр класса ClassJava и возвращает ссылку на этот объект. Ссылка помещается в переменную objJava.
После выполнения данной команды обращение к переменным и методам класса ClassJava производится так, как будто в переменной objJava содержится не ссылка, а экземпляр класса.
До сих пор не было сказано ни слова о модификаторах, которые могут указываться при объявлении переменных.
Модификатор типа доступа определяет, из каких классов может производиться обращение к объявляемой переменной. Доступ к переменным и методам в Javaорганизован несколько сложнее, чем в языке C, поэтому на первых этапах изучения языка руководствуйтесь простым правилом: чтобы разрешить обращение к переменной из-за пределов пакета, которому принадлежит класс, следует указать в описании этой переменной модификатор public.
Модификатор static превращает переменную экземпляра в переменную класса. Этот вид переменных хорошо знаком вам из языка C. В отличие от переменной экземпляра, переменная класса, или статическая переменная, с конкретным именем создаётся одна для всех объектов данного класса. Изменение значения этой переменной проявляется во всех экземплярах класса.
Модификатор final сообщает, что значение переменной не может изменяться. Другими словами, переменная final представляет собой не переменную в обычном смысле этого слова, а именованную константу.
Разговор о переменных, используемых внутри класса, закончим упоминанием о двух переменных, имеющих специальное назначение.
Переменная this хорошо известна вам из языка C. Она ссылается па текущий экземпляр класса. Наряду с переменной this в Java определена переменная super, которая указывает на суперкласс данного класса и может быть использована для вызова любых методов суперкласса.
Массивы.
Работа с массивами в Java существенно отличается от C/C++. Основное отличие состоит в том, что массив по сути представляет собой объект. Поэтому, для того чтобы создать массив, надо не только объявить переменную, но и выполнить оператор new.
Переменная, содержащая ссылку на массив, объявляется следующим образом:
тип имя_переменной [];
Как и при объявлении обычной переменной, перед определением типа могут быть указаны модификаторы static, final, public и т. д.
Для создания массива используется следующее выражение:
имя_переменной=new тип [размер];
В результате выполнения данного выражения создается массив, содержащий указанное число элементов, и ссылка на него помещается в переменную с заданным именем.
Методы.
Методы Java почти не отличаются от функций-членов C. Объявление метода выглядит следующим образом:
[модификаторы] возвращаемое_значение имя_метода ([список_параметров]) [throws список_исключений]
{
тело_метода
}
Назначение некоторых модификаторов описано ниже.
Модификатор типа доступа определяет, из каких классов может производиться обращение к методу. Как и в случае переменных, на первых этапах изучения языка вам достаточно запомнить модификатор public, допускающий вызов метода из-за пределов пакета.
Модификатор static определяет метод класса, или статический метод. В отличие от метода экземпляра, метод класса можно вызвать, даже если ни один экземпляр данного класса не был создан.
Модификатор abstract определяет абстрактный метод. Абстрактный метод должен обязательно быть переопределен в подклассе данного класса. Класс, содержащий хотя бы один абстрактный метод, является абстрактным классом; экземпляр такого класса не может быть создан.
В определении метода может присутствовать ключевое слово throws, сопровождаемое списком исключений. Исключения, или исключительные ситуации — чрезвычайно важный механизм, не владея которым практически невозможно построить приложение или апплет, выполняющие полезные действия.
Исключительные ситуации.
В процессе работы программы могут возникать ситуации, нарушающие нормальный ход вычислений: деление на нуль, ошибка обмена, ошибка при обращении по сети, попытка создать экземпляр абстрактного класса и т. д. Такие ситуации принято называть исключительными ситуациями, или исключениями.
По умолчанию в подобных случаях интерпретатор Java выводит сообщение, описывающее исключительную ситуацию, и завершает работу программы.
Учитывая, что ряд Java-программ выполняется под управлением других систем, становится ясно, что описанная реакция на возникновение исключительных ситуаций не всегда приемлема. Чтобы изменить порядок обработки исключений, надо при создании программы принять описанные ниже меры.
Фрагменты кода, которые могут генерировать исключительные ситуации, следует поместить в блок try следующего формата:
try
{
…
Последовательность команд
…
}
Непосредственно за блоком try должен размещаться как минимум один блок catch, который выглядит так:
catch (описание_исключения)
{
…
Команды обработки исключительной ситуации
…
}
При нормальной работе программы наличие блока try никак не сказывается на её выполнении. По завершении фрагмента, предшествующего блоку try, управление передается внутрь блока, а после того как все команды внутри блока будут выполнены, управление перейдет к первой команде, следующей после блока catch.
Для каждой категории исключительных ситуаций предусмотрен специальный класс, содержащий описания исключений. Так, например, арифметические ошибки описываются классом ArithmeticException, некорректная попытка создания объекта URL - классом с названием MalformedURLException и т. д.
Классы, которые описывают интересующие нас исключительные ситуации, являются подклассами класса Exception. Класс Exception, в свою очередь, является подклассом класса Throwable.
Иерархическая структура, включающая некоторые (очень немногие) классы, описывающие исключительные ситуации, представлена ниже.
Exception
!
!-- IOException
! !
! !-- MalformedURLException
! !
! !-- UnknownHostException
! …
!
!-- RuntimeException
!
!-- ArithmeticException
!
…
Рассмотрим блок try, содержащий несколько команд, и блоки catch, предназначенные для обработки исключительных ситуаций.
try
{
…
sock=new Socket (www.myserver.com, 80};
…
a=b/d;
…
}
catch (UnknownHostException ex)
{
Обработка исключительной ситуации UnknownHostException
}
catch (IOException ex)
{
Обработка исключительной ситуации IOException
}
catch (ArithmeticException ex)
{
Обработка исключительной ситуации ArithmeticException
}
За блоком try следуют три блока catch. Конструктор класса Socket, вызываемый в блоке try, может генерировать исключения UnknownHostException и IOException, а при делении b/d, если значение d будет равно нулю, возникнет исключительная ситуация ArithmeticException. Каждое из этих исключений обрабатывается отдельным блоком catch.
Однако код программы можно существенно сократить. Три блока catch можно заменить одним блоком, представленным ниже.
catch (Exception ex)
{
Обработка исключительных ситуаций всех типов
}
Если при выполнении метода может возникнуть исключительная ситуация, которая не обрабатывается в теле метода, необходимо специально оговорить это в тексте программы. При объявлении такого метода следует указать ключевое слово throws, сопровождаемое перечнем имён классов, описывающих исключительные ситуации, которые могут возникнуть при работе метода.
Простейшее Java-приложение.
Сейчас мы рассмотрим исходный код и процедуру построения простейшего Java-приложения. Текст программы, действия которой ограничиваются выводом краткого сообщения, приведён ниже.
import java.lang.*;
class FirstApplication
{
public static void main (String args [])
{
System.out.println (“Моё первое Java-приложение”);
}
}
Рассмотрим исходный текст приложения. Первая строка исходного кода импортирует пакет java.lang, которому принадлежит класс System, используемый в работе приложения.
Рассмотрим метод main (). В его объявлении указаны модификаторы public и static. Модификатор public позволяет вызывать метод из-за пределов текущего пакета. Модификатор static объявляет main () как статический метод. Это необходимо, так как при вызове main () ни одного экземпляра класса FirstApplication ещё не существует.
При вызове метода main () ему передастся массив строковых значений, в котором содержатся параметры, заданные в командной строке. В нашем примере обработка параметров не предусмотрена.
Единственная команда, содержащаяся в теле метода, выводит на экран строку текста. Последовательность имен System.out.println означает следующее. В классе System определена переменная класса, или статическая переменная out. Она ссылается на экземпляр класса PrintStream, реализующий выходной поток. В классе PrintStream определён метод println (), который выводит на экран строку символов, переданную ему в качестве параметра, и дополнительно выводит символ перевода строки.
Зная теперь, как работает данное приложение, поговорим о том, что надо сделать, чтобы запустить его на выполнение. Начнем с выбора инструментальных средств.
В настоящее время разработчику предлагаются самые разнообразные инструментальные пакеты, в том числе визуальные средства разработки программ. Выбор конкретного пакета — дело вкуса каждого специалиста. Однако среди всего набора доступных средств разработки есть пакет JDK (Java Development Kit — инструментальный пакет разработки Java-программ), выполняющий роль стандарта. Байтовый код Java-программы подготавливается с помощью компилятора, а выполняется под управлением интерпретатора.
Компилятор, входящий в состав JDK, называется javac. Он вызывается из командной строки, а в качестве параметра ему передается имя файла, содержащего исходный код Java-программы. Если приведенный выше исходный текст хранится в файле FirstApplication.java, то команда вызова компилятора будет выглядеть следующим образом:
javac FirstApplication.java
При определении класса FirstApplication не был указан модификатор public, поэтому на имя файла не налагается ограничений и исходный код приложения может храниться в файле с любым именем.
По окончании работы компилятор создаст байтовый код программы и поместит его в файл с именем FirstApplication.class. При выборе имени для файла с байтовым кодом учитывается не имя исходного файла, а имя класса. Если вы поместите исходный код в файл с другим именем, в результате компиляции все равно будет создан файл с именем FirstApplication.class.
После завершения работы компилятора байтовый код готов к выполнению. Для запуска Java-программы необходим интерпретатор, реализующий виртуальную машину Java (JVM). Средства поддержки JVM имеются в любом современном Web-браузере, однако для запуска приложения вам придется воспользоваться интерпретатором из пакета JDK и именем java. Как и javac, интерпретатор запускается из командной строки, а в качестве параметра задается имя класса, содержащего метод main (). В данном случае это класс FirstApplication.
Как и следует ожидать, в результате выполнения данного приложения на экране отобразится строка символов «Моё первое Java-приложение».
Интерфейсы.
Как вы, вероятно, помните, в определении класса может присутствовать ключевое слово implements, за которым следует имя интерфейса. Интерфейс — это класс, содержащий лишь абстрактные методы. Если имя такого класса указано за ключевым словом implements, в создаваемом классе необходимо переопределить все методы, объявленные в составе интерфейса.
Выполнение некоторых типов программ начинается по инициативе другого приложения, которое обращается не к main (), а к иному методу.
Пакеты.
Механизм пакетов позволяет группировать классы в соответствии с выполняемыми ими функциями. Чтобы понять, зачем в языке Java реализована поддержка пакетов, рассмотрим следующий пример:
import java.lang. *;
class Display
{
public static void main (String args [])
{
Calculations calc = new Calculations ();
System.out.println (calc.factorial (10));
}
}
class Calculations
{
int factorial (int a)
{
if (a ==1)
return 1;
else
return (a * factorial (a-1));
}
}
В отличие от предыдущего примера, данное приложение содержит два класса. Метод main (), определенный в классе Display, отображает данные, а factorial () — единственный метод, принадлежащий классу Calculations, вычисляет факториал целого числа.
Скомпилировав приложение и запустив его на выполнение, вы не увидите ничего особенного. Однако обратите внимание на содержимое текущего каталога. Если перед началом компиляции в нём был лишь файл Display.java, то теперь в нем находятся также файлы Display.class и Calculations.class.
Итак, в языке Java каждый класс располагается в отдельном файле.
После запуска приложения виртуальная машина Java сама находит файл Calculations.class и использует содержащийся в нём байтовый код для проведения вычислений. Это позволяет обойтись без этапа компоновки различных модулей.
Вспомните, как компилируются программы, написанные на языке C++. Код, который поддерживает объекты, используемые в программе, включается в состав исполняемого файла. Чтобы перенести C++-приложение на другой компьютер, необходимо скопировать весь исполняемый код, включая статически связанные библиотеки, — в противном случае приложение не будет работать. При использовании локальной сети такое копирование не вызывает затруднений, однако передать по Internet приложение объёмом в несколько мегабайт иногда бывает сложно.
Чтобы перенести на другой компьютер приложение, написанное на языке Java, надо передать значительно меньший объём кода. Необходимо скопировать лишь классы, отсутствующие на удаленной машине; обычно это именно те классы, которые определяют специфику написанного вами приложения. Малая загрузка линий связи при копировании программ — одна из особенностей, благодаря которым Java стал признанным «сетевым» языком.
Отсутствие редактирования связей — серьезное преимущество, однако в некоторых случаях оно может обернуться недостатком. Избежать путаницы в определениях классов позволяет механизм пакетов.
Первая строка каждого из приведенных выше исходных текстов содержала выражение
package math;
В результате все созданные классы были помещены в именованный пакет с именем math. Ваш коллега, используя подобную команду, может поместить созданный им класс в другой пакет
package stat;
Теперь полное имя класса, созданного вами, — math.Calculations, а имя класса, разработанного вашим сотрудником, — stat.Calculations.
Java поддерживает иерархию пакетов.