import p1.*;
Напомним, что импортировать разрешается только открытые классы, помеченные модификатором public.
Внимательный читатель и тут настороже. Мы ведь пользовались методами классов стандартной библиотеки, не указывая ее пакетов? Да, правильно.
Пакет java.lang просматривается всегда, его необязательно импортировать. Остальные пакеты стандартной библиотеки надо указывать в операторах import, либо записывать полные имена классов.
Начиная с версии Java SE 5 в язык введена еще одна форма оператора import, предназначенная для поиска статических полей и методов класса — оператор import static. Например, можно написать оператор
import static java.lang.Math.*;
После этого все статические поля и методы класса Math можно использовать без указания имени класса. Вместо записи
double r = Math.cos(Math.PI * alpha);
как мы делали раньше, можно записать просто
double r = cos(PI * alpha);
Подчеркнем, что оператор import вводится только для удобства программистов и слово "импортировать" не означает никаких перемещений классов.
Знатокам C/C++
Оператор import не эквивалентен директиве препроцессора include — он не подключает никакие файлы.
Java-файлы
Теперь можно описать структуру исходного файла с текстом программы на языке Java.
□ В первой строке файла может быть необязательный оператор package.
□ В следующих строках могут быть необязательные операторы import.
□ Далее идут описания классов и интерфейсов.
Еще два правила.
□ Среди классов файла может быть только один открытый public-класс.
□ Имя файла должно совпадать с именем открытого класса, если последний существует.
Отсюда следует, что если в проекте есть несколько открытых классов, то они должны находиться в разных файлах.
Соглашение "Code Conventions" рекомендует открытый класс, если он имеется в файле, описывать первым.
Для технологии Java характерно записывать исходный текст каждого класса в отдельном файле. В конце концов, компилятор всегда создает class-файл для каждого класса.
Интерфейсы
Вы уже заметили, что сделать расширение можно только от одного класса, каждый класс в или с происходит из неполной семьи, как показано на рис. 3.4, а. Все классы происходят только от "Адама", от класса Object. Но часто возникает необходимость породить класс D от двух классов в и с, как показано на рис. 3.4, б. Это называется множественным наследованием (multiple inheritance). В множественном наследовании нет ничего плохого. Трудности возникают, если классы в и с сами порождены от одного класса а, как показано на рис. 3.4, в. Это так называемое "ромбовидное" наследование.
А |
В С |
А |
Л |
V |
|
в с |
D |
D |
а) |
б) |
в) |
Рис. 3.4.Разные варианты наследования |
В самом деле, пусть в классе а определен метод f(), к которому мы обращаемся из некоторого метода класса D. Можем мы быть уверены, что метод f() выполняет то, что написано в классе а, т. е. это метод A.f()? Может, он переопределен в классах в и с? Если так, то каким вариантом мы пользуемся: B.f() или C.f() ? Конечно, допустимо определить экземпляры классов и обращаться к методам этих экземпляров, но это совсем другая ситуация.
В различных языках программирования этот вопрос решается по-разному, главным образом уточнением имени метода f(). Но при этом всегда нарушается принцип KISS. Вокруг множественного наследования всегда много споров, есть его ярые приверженцы и столь же ярые противники. Не будем встревать в эти споры, наше дело — наилучшим образом использовать средства языка для решения своих задач.
Создатели языка Java после долгих споров и размышлений поступили радикально — запретили множественное наследование классов вообще. При расширении класса после слова extends можно написать только одно имя суперкласса. С помощью уточнения super можно обратиться только к членам непосредственного суперкласса.
Но что делать, если все-таки при порождении надо использовать несколько предков? Например, у нас есть общий класс автомобилей Automobile, от которого можно породить класс грузовиков Truck и класс легковых автомобилей Car. Но вот надо описать пикап Pickup. Этот класс должен наследовать свойства и грузовых, и легковых автомобилей.
В таких случаях используется еще одна конструкция языка Java — интерфейс. Внимательно проанализировав ромбовидное наследование, теоретики ООП выяснили, что проблему создает только реализация методов, а не их описание.
Читать дальше
Конец ознакомительного отрывка
Купить книгу