Внимание. В слайде была ошибка. 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
} // конец тела класса
Переменные (ссылки), согласно документации, делятся на такие типы:
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
.
theAdam
;double NUMBER_PI = 3,14
;a - z
, _
, $
;#
, :
, /
и прочих символов, но могут содержать числа (не в начале!);int new;
, но можно объявить int newCounter;
; нельзя объявить String true;
, но можно объявить String someTrueString;
;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
:
theAdam
;name
, который по сути есть ссылка на объект класса String
;String
есть метод length()
, к которому мы и обратились; он вернет число символов в текущем объекте;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
;new Human()
.
Но лучше сразу объявить ссылку и передать ей этот объект: Human human = new Human();
.human.age = 12;
или System.out.println(human.age);
.
Доступ к полям объекта осуществляется через точку. Точка раскрывает объект и позволят добраться к полям этого объекта.
human = null;
или так human = new Human();
. Суть в том, что ссылка human
больше не ссылается на тот же объект;Итак, у класса есть три этапа жизненного цикла: design, implementation, usage. А у объектов их четыре: creation, living, releasing, removing.
Мы еще будем детально рассматривать stack и heap в грядущих уроках про методы. А пока взглянем на это в общих чертах для общего понимания. Ведь объекты эти существуют не в вакууме.
JVM разделяет свою память на stack & heap(куча). Стек служит для обеспечения работы методов. У каждой программы, у каждого потока свой стек. В стеке создаются фреймы вызываемых методов. А куча служит для хранения и доступа к объектам (одна куча на всю программу). Для простоты понимания:
Примитивы хранятся как в стеке, так и в хипе. Это зависит от места их объявления: в методе или на уровне класса. А объекты ссылочных типов хранятся только в хипе.
Рассмотрим такой пример:
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";
мы присваиваем этим ссылкам конкретные объекты.
В хипе это выглядит так:
int age
значение хранится в пределах объекта human
, а сам объект в хипе.String name
значение хранится где-то в хипе, так же как и human
. Может даже в соседнем регистре памяти JVM. А в пределах объекта human
просто записан адрес объекта в поле name
. Поскольку тип String
является ссылочным типом, а не примитивом.Перед тем как пойти далее, решать тесты, поэкспериментируйте с кодом в примерах этого урока. Посмотрите что будет с программой, если поменять местами инициализацию, оставить поля без значений, вывести несколько разных объектов на экран и т.п.
Именно эксперименты над кодом позволяют компенсировать недопонимание чего-либо в программировании. И в работе это приходиться делать частенько.
Дополнительная информация про стек и хип.
Для более глубокого понимания, того как объекты занимают память, рекомендую прочитать вот эту статью.
Вам ответят команда поддержки Хекслета или другие студенты.
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
Наши выпускники работают в компаниях:
Зарегистрируйтесь или войдите в свой аккаунт