Java для школьников. Занятие №10. Инициализация полей класса, использование экземпляров класса в качестве полей (метод композиции). Примитивные типы данных в Java

Предыдущее занятие Следущее занятие

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

  • метод композиции как вариант повторного использования кода в Java (использование экземпляров класса другим классом);
  • инициализация переменных класса или, по-другому: как присвоить переменной класса какое-либо начальное значение.

Еще поговорим о методе toString() и о "примитивных" типах.


Занятие построено на переработанном примере, который подсмотрен в книге: Б.Эккель "Философия Java", 4 издание - "Синтаксис композиции" - [1] в списке литературы.

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

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

Давайте разберем один из вариантов повторного использования кода в Java, который называется "композиция".

Листинг 1. Файл SprinklerSystem.java

class WaterSource {
        private String s;
        WaterSource() {
                System.out.println("WaterSource()");
                s="designed";
        }
        public String toString(){return s;}
}      
public class SprinklerSystem {
        private String valve1, valve2, valve3, valve4;
        WaterSource source = new WaterSource();
        private int i;
        private double f;
        SprinklerSystem(){ f= 15.3; valve2="2-way valve";};
        public String toString() {
                return "valve1=" + valve1 + " valve2=" +valve2 + " valve3=" + valve3 + " valve4=" +valve4 + "\n" + " i=" + i + " f=" + f + " source=" + source;
        }      
        public static void main (String args[]) {
                SprinklerSystem sprinklers = new SprinklerSystem();
                System.out.println(sprinklers);
        }      
}              

Итак, смотрим файл SprinklerSystem.java из Листинга 1. Наша программа состоит из двух классов: основного - SprinklerSystem (собственно сама система орошения) и вспомогательного WaterSource (источник воды). И в качестве поля класса SprinklerSystem, наряду с строковыми полями valve1, valve2, valve3, valve4 (valve - это кран или вентиль в нашей системе), полем целочисленного типа int - i и полем типа double - f используется поле с названием source, являющееся экземпляром класса WaterSource.

Оказывается, наряду с полями, являющимися так называемыми "примитивными" типами языка Java (int, double и т.д.), в качестве поля класса может быть использован экземпляр другого класса. Если быть честным, то тип String - это тоже представитель классов Java, только этот класс разработан уже не нами, а создателями языка. В Занятии 5 мы говорили также о выделении памяти для объектов. И если в состав класса входит экземпляр другого класса, то точно также, при создании нового объекта, память выделяется и для него. А код "внедряемого" объекта просто включается в новый класс (отсюда собственно и название такого метода повторного использования кода).

Для простоты наш класс WaterSource, который используется для композиции, содержит всего два метода - конструктор, который просто выводит с помощью System.out.println() строку с названием конструктора класса "WaterSource()" и метод toString(), который возвращает значение строковой переменной s. Интересно, что метод toString() мы определили и для основного класса. Ничего удивительного, ведь этот метод используется в Java в тех случаях, когда компилятор располагает не объектом, а, процитируем Эккеля:

"хочет получить его строковое представление в формате String. Поэтому в выражении метода SprinklerSystem.toString() " source=" + source; компилятор видит, что к строке " source=" прибавляется объект класса WaterSource. Компилятор не может это сделать, поскольку к строке можно "добавить" такую же строку, и преобразует объект source в String вызывая метод toString()...Чтобы подобное поведение поддерживалось вашим классом, достаточно включить в него метод toString()."

Поговорим теперь о значениях по умолчанию для полей классов. В терминах ООП процесс присвоения полю или переменной к.л. начального значения называют "инициализацией". Если скомпилировать этот пример и посмотреть на вывод программы, то мы увидим, что примитивные типы, определенные в качестве полей класса (переменная i) автоматически инициализируются нулевыми значениями. Ссылки на объекты заполняются значениями null, и при попытке вызова метода по этой ссылке произойдет исключение или по-просту - "произойдет ошибка во время исполнения программы". Однако, как оказалось, такую ссылку можно вывести на экран с помощью метода System.out.println.

ВОПРОС: после компиляции и выполнения программы (обязательно сделайте это) чему равно значение переменной valve2? Почему?

Компилятор не создает объекты для переменных класса и ссылок "по-умолчанию", и это логично, потому что во многих случаях это привело бы к лишним затратам ресурсов. Если нужно проинициализировать переменную - присвойте ей значение в конструкторе (как для f, valve2), ссылку на объект (класс) - воспользуйтесь оператором new.


В заключении, приведем в таблице примитивные типы Java, хоть мы еще и не со всеми познакомились:

Примитивный тип Размер занимаемой памяти, бит Минимум Максимум
boolean (логическое значение) - - -
char (символьное значение) 16 0 216-1
byte (байт) 8 -128 +127
short (короткое целое) 16 -215 +215-1
int (целое) 32 -231 +231-1
long (длинное целое) 64 -263 +263-1
float (число с плавающей запятой) 32 IEEE754 IEEE754
double (число с повышенной точностью) 64 IEEE754 IEEE754
void ("пустое" значение) - - -

Примечания: * для определения максимального значений переменных примитивных типов используется известная в информатике формула Хартли N = 2i, где i - размер переменной в битах, плюс необходимо учитывать еще знак переменной;
* IEEE754 - стандарт для представления чисел с плавающей запятой
Числа с плавающей точкой/запятой согласно стандарту IEEE754-2008, исчерпывающая информация

По ссылке ниже можно скачать пример класса Car, подсмотренного на просторах Интернета и немного переработанного, где с помощью нескольких вспомогательных классов показаны различные способы выполнения полезных действий в программе с использованием метода композиции. Скачать файл Car.java

Скачать файл Ferrari.java

1. Брюс Эккель "Философия Java", 4-е издание, Глава 7 "Повторное использование классов", параграф - "Синтаксис композиции" с.170