Программирование на Java
Шрифт:
Чтобы показать, что объект не потерял никаких свойств, произведем следующее обращение:
((Child)p).newChildMethod;
Здесь в начале проводится явное сужение к типу Child. Во время исполнения программы JVM проверит, совместим ли тип объекта, на который ссылается переменная p, с типом Child. В нашем случае это именно так. В результате получается ссылка типа Child, поэтому становится допустимым вызов метода newChildMethod, который вызывается у объекта, созданного в предыдущей строке.
Обратим внимание на важный частный случай - переменная типа Object может ссылаться на объекты любого типа.
В дальнейшем, с изучением новых типов (абстрактных классов, интерфейсов,
Таблица 4.1. Целочисленные типы данных.
Тип переменной | Допустимые типы ее значения |
---|---|
Примитивный | В точности совпадает с типом переменной |
Ссылочный | * null * совпадающий с типом переменной * классы-наследники от типа переменной |
Object | * null * любой ссылочный |
Заключение
В этой лекции были рассмотрены правила работы с типами данных в строго типизированном языке Java. Поскольку компилятор строго отслеживает тип каждой переменной и каждого выражения, в случае изменения этого типа необходимо четко понимать, какие действия допустимы, а какие нет, с точки зрения компилятора и виртуальной машины.
Были рассмотрены все виды приведения типов в Java, то есть переход от одного типа к другому. Они разбиваются на 7 групп, начиная с тождественного и заканчивая запрещенными. Основные 4 вида определяются сужающими или расширяющими переходами между простыми или ссылочными типами. Важно помнить, что при явном сужении числовых типов старшие биты просто отбрасываются, что порой приводит к неожиданному результату. Что касается преобразования ссылочных значений, то здесь действует правило - преобразование никогда не порождает новых и не изменяет существующих объектов. Меняется лишь способ работы с ними.
Особенным в Java является преобразование к строке.
Затем были рассмотрены все ситуации в программе, где могут происходить преобразования типов. Прежде всего, это присвоение значений, когда преобразование зачастую происходит незаметно для программиста. Вызов метода во многом похож на инициализацию. Явное приведение позволяет осуществить желаемый переход в том случае, когда компилятор не позволяет сделать это неявно. Преобразование при выполнении числовых операций оказывает существенное влияние на результат.
В заключение была рассмотрена связь между типом переменной и типом ее значения.
8. Лекция: Объектная модель в Java
Эта лекция является некоторым отступлением от рассмотрения технических особенностей Java и посвящена в основном изучению ключевых свойств объектной модели Java, таких как статические элементы, абстрактные методы и классы, интерфейсы, являющиеся альтернативой множественного наследования. Без этих мощных конструкций язык Java был бы неспособен решать серьезные задачи. В заключение рассматриваются принципы работы полиморфизма для полей и методов, статических и динамических. Уточняется классификация типов переменных и типов значений, которые они могут хранить.
Статические элементы
До этого момента под полями объекта мы всегда понимали значения, которые имеют смысл только в контексте некоторого экземпляра класса. Например:
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");
}
}
Если объявление статического поля совмещается с его инициализацией, то поле инициализируется также однократно при загрузке класса. На объявление и применение статических полей накладываются те же ограничения, что и для динамических,– нельзя использовать поле в инициализаторах других полей или в инициализаторах класса до того, как это поле объявлено: