Зарегистрируйтесь для доступа к 15+ бесплатным курсам по программированию с тренажером

Модуль 2. Урок 3. Работа с экземплярами в Java. Введение в Java

Видео может быть заблокировано из-за расширений браузера. В статье вы найдете решение этой проблемы.

... Внимание. В слайде была ошибка. time: 01:48 в классе Main метод main должен быть void, а не voind.

В этом уроке много базовых терминов. Их нужно знать как алфавит. Вам придется неоднократно возвращаться и повторять этот материал — это нормально. :)


Java Virtual Machine (JVM) — это программная система, в которой исполняются java-программы. Для java-программ JVM является реальной, а не виртуальной вычислительной машиной. А вот уже JVM написана под различные платформы и операционные системы.

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

класс — это тот самый blueprint в мире java, по которому будут создаваться объекты. Класс состоит из кода, который хранится в соответствующем файле.

Вот пример кода класса Human, который должен обязательно находится в файле Human.java:

class Human {
    int age = 99;
    String name = "Oldman";
}

На основе этих строк будут создаваться объекты.

члены класса — это основные элементы, которые может содержать в себе класс. Ими являются переменные (поля) и методы.

экземпляр, объект, instance — это сущность созданная на основе класса. Так же как и деталь на токарном станке создается на основе чертежа или человек "создается" на основе ДНК. Например, объект класса Object создается так: new Object(). А объект класса Human — new Human(). В мире java все является объектом.

оператор new выделяет место в памяти JVM под объект.

идентификатор (ссылка на документацию) — это, по сути, имя. Такое же, как и у людей, мест, объектов во всем мире. Но в документации java применяется термин "identifier". Потому что он позволяет идентифицировать конкретную сущность в мире java. В java идентификатор используется для переменных, методов, классов. Например, Object, String, Human, SomeClassName — это примеры идентификаторов (имен) классов. Имена классов всегда пишутся с большой буквы в camelStyle. Имена методов мы разберем в отдельном уроке. А имена переменных — в этом.

метод — это член класса в котором описывается логика (последовательность действий) над данными. О методах мы поговорим в других уроках.

reference, ссылка, переменная — это идентификатор (имя), с заданным типом, который может указывать на конкретный объект в оперативной памяти или на значение примитива. Ссылку часто называют переменной, но это не всегда соответствует ее свойствам. Важно понимать, что любая переменная предназначена для хранения адреса в памяти на конкретный объект или значение примитива, потому — ссылка на что-то. Правила именования ссылок будут даны ниже. При объявлении ссылки, естественно, нужно указать ее тип и имя. Например, так: Object myObject. Object — это тип, который сможет хранить в себе данная ссылка. myObject — это имя. Но эта ссылка, пока что, указывает в никуда, в null. Ей нужно присвоить объект — инициализировать.

null — это специальное ключевое слово, которое означает буквально "ничто".

инициализация переменной — это присвоение ссылке адреса конкретного объекта. Делается это с помощью знака равно "=". В java это оператор присваивания. Запишем объявление ссылки, ее инициализацию и создание объекта в одну строчку: Object otherObject = new Object();.

На один и тот же объект могут ссылаться несколько ссылок:

Human firstHuman = new Human();
Human secondHuman = firstHuman;
Human thirdHuman = firstHuman;

В данном кусочке кода создан один объект типа Human и присвоен трем ссылкам (передан его адрес в памяти по цепочке). А вот если написать напротив каждой ссылки new Human() — то у каждой ссылки будет свой объект.

У одной ссылки (идентификатора) — может быть только один объект!

блок кода — это часть кода, заключенная в фигурные скобки: {}. { — открывает блок кода. } — закрывает блок кода. Класс, метод, цикл, условный оператор — имеют свои блоки кода. Блок кода каждого из них, для простоты, называют телами. Методы, циклы, условные операторы — мы рассмотрим в других уроках. А сейчас давайте просто посмотрим на их тела в таком примере:

class Computer { // начало тела класса
    String foo = "просто пример поля класса";

    int computeSumm(int[] incomeArray) { // начало тела метода computeSumm
        int result = 0;
        if (incomeArray != null) { // начало тела условного оператора
            for (int number : incomeArray) { // начало тела цикла
                result += number;
            } //конец тела цикла
        } // конец тела условного оператора
        return result;
    } // конец тела метода computeSumm

    void printFoo() { // начало тела метода printFoo
        System.out.println(foo);
    } // конец тела метода printFoo
} // конец тела класса

Переменные (ссылки), согласно документации, делятся на такие типы:

  • Поля класса. Это ссылки, которые объявлены в теле класса. Если взять в пример класс Human, то его полями будут name и age. В классе Computer это будет поле с именем foo. Объявление и инициализация поля класса не может быть разбито на две строки в рамках тела класса! Например, нельзя написать так: ```java class Computer2 { String foo; // объявляем поле класса foo = "просто пример поля класса"; // безуспешно пытаемся его инициализировать, ошибка компиляции! }
Но можно вынести инициализацию в другой блок кода так:
```java
class Computer3 { // начало тела класса
    String foo; // объявляем поле класса

    void printFoo() { // начало тела метода printFoo
        foo = "просто пример поля класса"; // успешно инициализируем поле foo в теле метода
        System.out.println(foo); // печатаем на экран содержимое foo
    } // конец тела метода printFoo
} // конец тела класса

Где, как и когда инициализировать поля класса — зависит от условий задачи, которую Вы решаете.

  • Локальные переменные. Это ссылки, которые объявлены в любом блоке кода, который вложен в тело класса. Если рассмотреть класс Computer, то в нем локальной переменной будет result, поскольку эта ссылка объявлена внутри тела метода computeSumm.
  • Параметры. Это ссылки, задающие начальное состояние блока кода. Например, у метода computeSumm есть свой параметр incomeArray, который имеет тип int[]. Параметры метода еще называют "аргументы метода". А вот number уже является параметром цикла for.

Любая переменная — это просто ссылка на конкретный объект или значение (примитив) в памяти компьютера.

область видимости — это границы, в которых доступны переменные для членов класса. Каждый блок кода видит переменные внешнего блока кода, но не видит содержимое внутренних блоков кода. Например, ссылка foo объявлена в теле класса Computer — она видна любым другим членам текущего класса и вложенным друг в друга блокам кода. То есть мы можем печатать переменную foo не только в методе printFoo, но и в методе computeSumm или даже в цикле for. А вот переменная (параметр метода computeSumm) incomeArray — видна только в рамках тела этого метода. Это значит, что класс Computer не знает о существовании incomeArray. И метод printFoo, соответственно, тоже не знает о существовании переменной incomeArray. Зато все вложенные в метод computeSumm блоки кода (циклы, условные операторы и все, что мы туда засунем) могут обращаться к incomeArray и использовать эту переменную. Аналогичной по видимости incomeArray является переменная result. А переменная number видна только в рамках тела цикла for.

Правила именования ссылок переменных

  1. имя ссылки всегда пишется с маленькой буквы;
  2. в верблюжем стиле и слитно, если более одного слова в имени ссылки, например, theAdam;
  3. константы пишутся в теле класса и ПОЛНОСТЬЮЗАГЛАВНЫМИБУКВАМИ с подчеркиванием между словами, например: double NUMBER_PI = 3,14;
  4. имена переменных могут начинаться с: a - z, _, $;
  5. имена не могут начинаться с любого числа или точки;
  6. имена не могут содержать в себе #, :, / и прочих символов, но могут содержать числа (не в начале!);
  7. имена не могут состоять только из ключевого слова синтаксиса java или литерала; например, нельзя объявить int new;, но можно объявить int newCounter;; нельзя объявить String true;, но можно объявить String someTrueString;;
  8. в теле одного блока кода не может быть полей с одинаковыми идентификаторами (именами)! Это значит, что если мы уже объявили в теле класса Computer поле String foo, то второй раз уже не нужно писать String foo в теле этого класса. Но зная об области видимости мы смело можем объявить еще один раз String foo, например, в рамках тела метода. Попробуйте это прямо сейчас в коде. Экспериментируйте и внимательно изучайте вывод компилятора — он дает много подсказок об ошибках в коде.

Ссылка на перечень ключевых слов в документации. Ознакомьтесь с ними.

Ссылка на перечень разновидностей литералов. Для начала, проще запомнить эти литералы, которые нельзя использовать как готовые имена: true, false, null.

Использование объектов

Объект можно создать только из ссылочного типа бесконечное количество раз (в пределах возможностей компьютера). Примитивные типы просто имеют уже конечный перечень значений, которые мы можем использовать, а создавать объекты на их основе примитивов — невозможно. Но есть "обертки" над примитивами, из которых можно создавать объекты. Обертки являются ссылочными типами данных (см. предыдущий урок).

Для начала рассмотрим пример создания объектов на основе класса Human. Изучите код:

public class Main {
    public static void main(String[] args) {
        Human theAdam; // Объявим ссылку типа Human. Эта запись равносильна `Human theAdam = null;`
        theAdam = new Human(); // создаем новый объект Human справа от = и присваиваем его адрес ссылке theAdam
        // Сделаем все то же самое но с Евой и в одну строчку:
        Human theEve = new Human(); // Кстати, Еву можно было объявить и в теле класса.

        // А теперь, ради фана, создадим тут же объект класса Main:
        Main main = new Main();
    }
}

Экспериментируйте с этим кодом. Покрутите его в IDEA. Только ж не забудьте еще написать класс Human. ;)

использование объекта — это любая форма взаимодействия с объектом. Например, обращение к его членам или передача объекта в качестве параметра (аргумента) метода.

Для обращения к объекту, почти всегда, необходимо имя его ссылки. Например, для обращения к одному из двух объектов класса Human нам нужно написать theAdam или theEve. А дальше мы можем получить доступ к членам этих объектов.

доступ к членам объектов осуществляется посредством обычной точки ".". Точка говорит: "Раскрыть этот объект!". А за точкой нужно указать идентификатор нужного нам члена объекта.

Например, обратимся к полю name объекта theAdam и назначим ему нормальное имя: theAdam.name = "Adam";. Так же можно поменять имя и для theEve: theEve.name = "Eve";. Рассмотрим это чуть ниже в примерах.

Обратите внимание, что поле name ссылается на конкретные объекты класса String. А это значит, что мы можем получить доступ к членам и этого объекта! Например так: theAdam.name.length(). Рассмотрим это в коде:

public class Main {
    public static void main(String[] args) {
        Human theAdam = new Human(); //объявили и инициализировали ссылку theAdam
        theAdam.name = "Adam"; // изменили содержимое поля name у объекта на который ссылается theAdam
        int lengthOfName = theAdam.name.length(); // объявили и инициализировали переменную lengthOfName
        System.out.println(lengthOfName); // вывод на экран: 4
    }
}

Рассмотрим инициализацию переменной lengthOfName:

  1. обращаемся к объекту переменной theAdam;
  2. "раскрыли" его точкой;
  3. обращаемся к полю name, который по сути есть ссылка на объект класса String;
  4. "раскрыли" его точкой;
  5. а у любого объекта класса String есть метод length(), к которому мы и обратились; он вернет число символов в текущем объекте;
  6. записываем результат работы нашей цепочки вызовов в локальную переменную lengthOfName.

Для обращения к объекту не всегда необходимо использовать имя его ссылки. Например, мы можем создать объект и тут же передать его в какой-то метод, при этом не сохраняя адрес этого объекта в коде: System.out.println(new Human()). И к его членам мы тоже можем обратиться. Рассмотрим в коде:

public class Main {
    public static void main(String[] args) {
        System.out.println(new Human()); // на экране будет что-то вроде: Human@1b6d3586
        System.out.println("Human.name: " + new Human().name); // на экране: Human.name: Oldman
        System.out.println("Human.age: " + new Human().age);  // на экране: Human.age: 99
    }
}

Как видно из этого примера, мы создали несколько объектов и ни одной ссылки на них. Так стоит поступать только и только тогда, когда мы точно знаем, что не станем повторно использовать один и тот же объект.

Что такое Human@1b6d3586 — это то, как JVM видит внутри себя конкретный объект класса Human. У каждого нового объекта будет свой адрес в памяти JVM на подобии Human@1b6d3586.

В данном случае, у каждого нового объекта класса Human всегда поле name будет содержать "Oldman", а age — 99. Потому что изначально в классе были "зашиты" эти параметры, для удобства примеров кода. В рабочем коде предопределять значения рекомендуется только КОНСТАНТАМ.

Жизненный цикл любого класса в Java

  • дизайн класса (design) — предварительная проектировка, до написания кода. Например, в UML-диаграмме;

HumanUML Предварительное описание классов и его членов — является хорошей практикой!

  • имплементация класса (реализация класса в редакторе) — описываем класс в виде кода в файле с расширением *.java;

human.java

  • использование класса — это создание объекта на его основе.

trololoshko.java.jpg

Жизненный цикл instances (объектов) класса

  • Создание (creation) в памяти нового instance, реального объекта, который создается на базе класса, например, так: new Human(). Но лучше сразу объявить ссылку и передать ей этот объект: Human human = new Human();.
  • Жизнь (living)/использование instance. Любые манипуляции с объектом, например, human.age = 12; или System.out.println(human.age);.

trololoshko_in_use.jpg

Доступ к полям объекта осуществляется через точку. Точка раскрывает объект и позволят добраться к полям этого объекта.

  • releasing (освобождать, отпускать, сбрасывать) — отвязываем текущий объект от ссылки, например, так human = null; или так human = new Human();. Суть в том, что ссылка human больше не ссылается на тот же объект;
  • уничтожение (removing) — объект удаляется из памяти мусорщиком, если на него не ссылается ни одной ссылки в текущей программе.

Итак, у класса есть три этапа жизненного цикла: design, implementation, usage. А у объектов их четыре: creation, living, releasing, removing.

Память и объекты

Мы еще будем детально рассматривать stack и heap в грядущих уроках про методы. А пока взглянем на это в общих чертах для общего понимания. Ведь объекты эти существуют не в вакууме.

JVM разделяет свою память на stack & heap(куча). Стек служит для обеспечения работы методов. У каждой программы, у каждого потока свой стек. В стеке создаются фреймы вызываемых методов. А куча служит для хранения и доступа к объектам (одна куча на всю программу). Для простоты понимания:

  • Стек удобно рассматривать как стопку монет, в которой каждая монета — это запущенный метод. Так называемый "фрейм метода". Пока не отработает верхняя монета — остальные недоступны. Работает стек по принципу LIFO. Повторюсь: детальнее рассмотрим это в другом уроке.
  • Куча хоть и говорящее название, но эту область памяти JVM лучше воспринимать как библиотеку или склад, в которой каждая книжка имеет свой адрес.

Примитивы хранятся как в стеке, так и в хипе. Это зависит от места их объявления: в методе или на уровне класса. А объекты ссылочных типов хранятся только в хипе.

Рассмотрим такой пример:

class Human {
    String name;
    int age;
}

public class Main {
    public static void main(String[] args) { // Метод main специальный. Он нужен для запуска программы и не обязан быть в каждом классе.
        int someNumber = 999999; // Этот примитив хранится в фрейме метода main, а фрейм метода — в стеке.
        Human human; // создали ссылку в стеке метода main. Она пока ссылается на `null`.
        human = new Human(); // ссылке присвоили адрес созданного в хипе объекта.
        human.age = 12; // Заполняем поле класса (использование).
        human.name = "Testor"; // Заполняем поле класса (использование).
        human = null; // Отвязали ссылку от объекта. Объект будет уничтожен мусорщиком (когда-нибудь), чем высвободит память, которую занимал.
    }
}

someNumber — эта ссылка находится в фрейме метода main, а фрейм в стеке. И память под значение 999999 тоже выделена в стеке, в фрейме текущего метода.

human — эта ссылка находится в стеке метода main. О стеке методов, повторюсь, мы узнаем больше в следующих уроках. И указывает human на адрес в хипе, где находится экземпляр (инстанс) класса Human.

Сам объект human содержит в себе еще две ссылки: name типа String и age типа int. В строчках human.age = 12; и human.name = "Testor"; мы присваиваем этим ссылкам конкретные объекты.

В хипе это выглядит так:

  1. Для int age значение хранится в пределах объекта human, а сам объект в хипе.
  2. Для String name значение хранится где-то в хипе, так же как и human. Может даже в соседнем регистре памяти JVM. А в пределах объекта human просто записан адрес объекта в поле name. Поскольку тип String является ссылочным типом, а не примитивом.

Рекомендация

Перед тем как пойти далее, решать тесты, поэкспериментируйте с кодом в примерах этого урока. Посмотрите что будет с программой, если поменять местами инициализацию, оставить поля без значений, вывести несколько разных объектов на экран и т.п.

Именно эксперименты над кодом позволяют компенсировать недопонимание чего-либо в программировании. И в работе это приходиться делать частенько.

Полезные ссылки


Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты.

Ошибки, сложный материал, вопросы >
Нашли опечатку или неточность?

Выделите текст, нажмите ctrl + enter и отправьте его нам. В течение нескольких дней мы исправим ошибку или улучшим формулировку.

Что-то не получается или материал кажется сложным?

Загляните в раздел «Обсуждение»:

  • задайте вопрос. Вы быстрее справитесь с трудностями и прокачаете навык постановки правильных вопросов, что пригодится и в учёбе, и в работе программистом;
  • расскажите о своих впечатлениях. Если курс слишком сложный, подробный отзыв поможет нам сделать его лучше;
  • изучите вопросы других учеников и ответы на них. Это база знаний, которой можно и нужно пользоваться.

Об обучении на Хекслете

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно.

  • 130 курсов, 2000+ часов теории
  • 900 практических заданий в браузере
  • 360 000 студентов
Даю согласие на обработку персональных данных, соглашаюсь с «Политикой конфиденциальности» и «Условиями оказания услуг»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff

Используйте Хекслет по максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Даю согласие на обработку персональных данных, соглашаюсь с «Политикой конфиденциальности» и «Условиями оказания услуг»