22 июн. 2015 г.

Классы. Часть 2 – подробнее о создании экземпляров классов (объектов).

В прошлой статье мы рассмотрели лишь основные моменты о классах. Теперь рассмотрим более подробно создание объектов (инстанцирование).

Как мы уже отмечали, при создании класса вы создаете новый тип данных. Этот тип можно использовать для объявления объектов данного типа. Однако создание объектов класса — двухступенчатый процесс. Вначале необходимо объявить переменную типа класса. Эта переменная не определяет объект. Она представляет собой всего лишь переменную, которая может ссылаться на объект. Затем потребуется создать сам объект в памяти и присвоить ссылку на него этой переменной. Это можно выполнить с помощью операции new. Эта операция динамически (т.е. во время выполнения) распределяет память под объект и возвращает ссылку на него. В общих чертах эта ссылка представляет собой адрес объекта в памяти. Затем эта ссылка сохраняется в переменной.

Рассмотрим эту операцию более подробно, на примере программы из прошлого поста. Создать экземпляр нашего класса Box можно следующим образом:

Box mybox = new Box();

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

Box mybox; // объявление ссылки на объект
mybox = new Box(); // распределение памяти для объекта Box

В первой строке приведенного выше фрагмента кода переменная mybox объявляется как ссылка на объект типа Вох. В данный момент переменная mybox пока еще не ссылается на конкретный объект.

Grabli

Тут начинается один интересный момент, где можно наступить на грабельки. Поэтому стоит еще раз напомнить, что если переменная (объекта или примитива) объявляется в методе, то перед ее использованием она должна быть проинициализирована каким-либо значением. Для объектов это может быть и null. Так после первой строки примера, в mybox не находится даже null, т.к. он был объявлен в методе. Если же он был бы объявлен как поле, то после создания объекта оператором new, который вызовет конструктор по умолчанию, там был бы null.

Важно понимать, что для переменных объявленных в методе, память выделается в стеке, а для полей класса в куче (heap'e). В стеке может быть любой "мусор" (не путать с ментом), поэтому перед использованием переменную необходимо инициализировать каким либо значением. Поле же класса создается в хипе (не путать с хиппи), и конструктор по умолчанию инициализирует все поля класса значениями по умолчанию для их типов данных.

В второй строке примера выделяется память для конкретного объекта оператором new, а переменной mybox присваивается ссылка на этот объект. После выполнения второй строки кода переменную mybox можно использовать так, как если бы она была объектом типа Вох. Но в действительности переменная mybox просто содержит адрес памяти (ссылку) конкретного объекта типа Вох.

N0001

Чтобы стало понятней рассмотрим пример слева и вывод этой программы ниже.

N0002

Как видим у нас есть поле filedBox класса Box и так же мы объявили переменную типа Box – mybox в методе main().

Так вот, поскольку поле filedBox было проинициализировано конструктором по умолчанию для класса Classes002, там уже находится null – значение по умолчанию для объектов. И поэтому мы можем вывести его на печать сразу же, что и делает первый оператор в методе main(). А вот с переменной mybox такой номер уже не пройдет, так как она была объявлена внутри метода, следовательно мы не сможем сразу же ее использовать, например вывести на печать, так как компилятор нам даже не позволит скомпилировать программу и выдаст ошибку, поэтому строка вывода на печать mybox сразу после его объявления закомментирована.

А уже после того как мы инициализируем mybox хотя бы значением null, то тогда уже можно использовать оператор вывода на печать.

Когда же мы уже создали полноценный объект типа Box в памяти при помощи оператора new и присвоили ссылку на него переменной mybox, то переменную mybox можно уже использовать как объект (на самом деле ссылку на объект) и вызвать методы класса, а так же обращаться к его полям (если они открыты).

N0003Теперь еще раз стоит вспомнить разницу между ссылочными и примитивными типами данных. И чтобы освежить это в памяти рассмотрим тут еще один пример.

В строке 29 на первый взгляд, переменной b2 присваивается ссылка на копию объекта, на которую ссылается переменная b1. Таким образом, может показаться, что переменные b1 и b2 ссылаются на совершенно разные объекты, но это совсем не так. После выполнения данного фрагмента кода обе переменные, b1 и b2, будут ссылаться на один и тот же объект. Присваивание переменной b2 значения переменной b1 не привело к выделению области памяти или копированию какой-нибудь части исходного объекта. Такое присваивание приводит лишь к тому, что переменная b2 ссылается на тотже объект, что и переменная b1 . Таким образом, любые изменения, внесенные в объекте по ссылке в переменной b2 , окажут влияние на объект, на который ссылается переменная b1 , поскольку это один и тот же объект. Что собственно и видно из вывода программы:

N0004

Как видно изменения сделанные в строке 30, как казалось бы над другим объектом b2, на самом деле были сделаны над объектом на который ссылаются b1 и b2, то есть над одним и тем же объектом. Это видно из вывода команды в строке 31. Но после того как мы в строке 32 создали новый объект и вернули ссылку на него в b2, b2 стало ссылаться на другой объект, а b1 сохранил свою ссылку на старый.

Ну и в заключение предлагаю еще раз посмотреть хорошее видео на данную тему:

Создание и использование объектов в Java

Комментариев нет:

Отправить комментарий