244 lines
22 KiB
TeX
244 lines
22 KiB
TeX
\documentclass[../j-spec.tex]{subfiles}
|
||
|
||
\begin{document}
|
||
\section{Специализация: ООП}
|
||
\begin{longtable}{|p{35mm}|p{135mm}|}
|
||
\hline
|
||
Экран & Слова \\ \hline
|
||
\endhead
|
||
|
||
Титул & Перейдём к интересному: что можно хранить в джаве, как оно там хранится, и как этим манипулировать \\ \hline
|
||
|
||
На прошлом уроке & На прошлом уроке мы рассмотрели базовый функционал языка, то есть основную встроенную функциональность, такую как математические операторы, условия, циклы, бинарные операторы. Также разобрали способы хранения и представления данных в Java, и в конце поговорили о способах манипуляции данными, то есть о функциях (в терминах языка называющиеся методами) \\ \hline
|
||
|
||
На этой лекции & После разбора типов данных попробуем с помощью примеров разобраться, что такое классы и объекты, а также с тем, как применять на практике основные принципы ООП: наследование, полиморфизм и инкапсуляцию. Дополнительно поговорим об устройстве памяти в джава. \\ \hline
|
||
|
||
Наследование — это передача всех свойств и поведения от одного класса другому, более конкретному. У карася и ерша, как и у всех рыб, есть плавники, хвосты, жабры и чешуя, они живут в воде и плавают;
|
||
Инкапсуляция — это размещение данных и методов для их обработки в одном объекте, а также сокрытие деталей его реализации. Мы знаем, как включать и выключать телевизор, переключать программы и регулировать громкость. Для этого не обязательно знать, как он устроен;
|
||
Полиморфизм — это проявление одного поведения разными способами. Животные могут издавать звуки, при этом кошка мяукает, а собака лает.
|
||
|
||
Чуть позже мы рассмотрим эти принципы подробнее. А сейчас разберём некоторые основы, которые мы будем использовать часто в данных принципах. Начнём мы с понятия "Класс".
|
||
Что такое класс? Класс определяет форму и сущность объекта и является логической конструкцией, на основе которой построен весь язык Java. Наиболее важная особенность класса состоит в том, что он определяет новый тип данных, которым можно воспользоваться для создания объектов этого типа, т.е. класс — это шаблон (чертеж), по которому создаются объекты (экземпляры класса). Для определения формы и сущности класса указываются данные, которые он должен содержать, а также код, воздействующий на эти данные.
|
||
Если мы хотим работать в нашем приложении с документами, то необходимо для начала объяснить что такое документ, описать его в виде класса (чертежа) Document. Рассказать какие у него должны быть свойства: название, содержание, количество страниц, информация о том, кем он подписан и т.д. В этом же классе мы описываем что можно делать с документами: печатать в консоль, подписывать, изменять содержание, название и т.д. Результатом такого описания и будет наш класс Document. Однако это по-прежнему всего лишь чертеж. Если нам нужны конкретные документы, то необходимо создавать объекты: документ №1, документ №2, документ №3. Все эти документы будут иметь одну и ту же структуру (название, содержание, …), с ними можно выполнять одни и те же действия, НО наполнение будет разным (например, в первом документе содержится приказ о назначении работника на должность, во втором, о выдаче премии отделу разработки и т.д.).
|
||
Начнём с малого, напишем свой первый класс.
|
||
Представим, что нам необходимо работать в нашем приложении с котами. Java ничего не знает о том, что такое коты, поэтому нам необходимо создать новый класс (тип данных), и объяснить что же такое кот.
|
||
Создадим проект, его структура нам не в новизну, и будет иметь следующий вид:
|
||
|
||
Disk:.
|
||
├───out
|
||
└───src
|
||
└───ru
|
||
└───gb
|
||
└───jcore
|
||
Cat.java
|
||
Main.java
|
||
|
||
|
||
Теперь начнем потихоньку прописывать класс Cat. Пусть у котов есть три свойства: name (кличка), color (цвет) и age (возраст); и они пока ничего не умеют делать.
|
||
Класс Cat имеет следующий вид, и как мы все прекрасно помним, имя класса должно совпадать с именем файла, в котором он объявлен, т.е. класс Cat должен находиться в файле Cat.java:
|
||
|
||
package ru.gb.jcore;
|
||
|
||
public class Cat {
|
||
String name;
|
||
String color;
|
||
int age;
|
||
}
|
||
|
||
|
||
Итак, мы рассказали Java что такое коты, теперь если мы хотим создать в нашем приложении кота, следует воспользоваться следующим оператором:
|
||
|
||
Cat cat1 = new Cat();
|
||
|
||
|
||
Подробный разбор того, что происходит в этой строке, будет проведен в следующем пункте. Пока же нам достаточно знать, что мы создали объект типа Cat (экземпляр класса Cat), и для того чтобы с ним работать, положили его в переменную, которой присвоили имя cat1. На самом деле, в переменной не лежит весь объект, а только ссылка где его искать в памяти, но об этом позже.
|
||
Объект cat1 создан по чертежу Cat, и значит у него есть поля name, color, age, с которыми можно работать (получать или изменять их значения). Для доступа к полям объекта служит операция-точка, которая связывает имя объекта с именем поля. Например, чтобы присвоить полю color объекта cat1 значение "Белый", нужно выполнить следующий оператор:
|
||
|
||
cat1.color = "Белый";
|
||
|
||
|
||
Операция-точка служит для доступа к полям и методам объекта по его имени. Рассмотрим пример консольного приложения, работающего с объектами класса Cat. Перейдём в главный класс Main и напишем следующий код:
|
||
|
||
package ru.gb.jcore;
|
||
|
||
public class Main{
|
||
public static void main(String[] args) {
|
||
Cat cat1 = new Cat();
|
||
Cat cat2 = new Cat();
|
||
|
||
cat1.name = "Барсик";
|
||
cat1.color = "Белый";
|
||
cat1.age = 4;
|
||
|
||
cat2.name = "Мурзик";
|
||
cat2.color = "Черный";
|
||
cat2.age = 6;
|
||
|
||
System.out.println("Кот 1 имя: " + cat1.name + " цвет: " + cat1.color + "
|
||
возраст: " + cat1.age);
|
||
System.out.println("Кот 2 имя: " + cat2.name + " цвет: " + cat2.color + "
|
||
возраст: " + cat2.age);
|
||
}
|
||
}
|
||
|
||
|
||
Выполним уже известные нам команды для компиляции и запуска приложения:
|
||
javac -sourcepath ./src -d out .\src\ru\gb\jcore\Main.java
|
||
java -classpath ./out ru.gb.jcore.Main
|
||
Выведется:
|
||
|
||
Кот 1 имя: Барсик цвет: Белый возраст: 4
|
||
Кот 2 имя: Мурзик цвет: Черный возраст: 6
|
||
|
||
|
||
Вначале мы создали два объекта типа Cat: cat1 и cat2, соответственно они имеют одинаковый набор полей (name, color, age), однако каждому из них мы в эти поля записали разные значения. Как видно из результата печати в консоле, изменение значения полей одного объекта, никак не влияет на значения полей другого объекта. Данные объектов cat1 и cat2 изолированы друг от друга.
|
||
|
||
ЧТО ТАКОЕ ОБЪЕКТ
|
||
Как создавать новые типы данных (классы) мы разобрались, мельком посмотрели и как создаются объекты наших классов. Давайте теперь поподробнее разберем как создавать объекты, и что при этом происходит
|
||
Создание объекта проходит в два этапа. Сначала создается переменная, имеющая интересующий нас тип (в данном случае Cat), в нее мы сможем записать ссылку на будущий объект (поэтому при работе с классами и объектами мы говорим о ссылочных типах данных). Затем необходимо выделить память под наш объект, создать и положить объект в выделенную часть памяти, и сохранить ссылку на этот объект в памяти в нашу переменную.
|
||
Для непосредственного создания объекта применяется оператор new, который динамически резервирует память под объект и возвращает ссылку на него, в общих чертах эта ссылка представляет собой адрес объекта в памяти, зарезервированной оператором new.
|
||
|
||
public static void main(String[] args) {
|
||
Cat cat1;
|
||
cat1 = new Cat();
|
||
}
|
||
|
||
|
||
В первой строке кода переменная cat1 объявляется как ссылка на объект типа Cat и пока ещё не ссылается на конкретный объект (первоначально значение переменной cat1 равно null). В следующей строке выделяется память для объекта типа Cat, и в переменную cat1 сохраняется ссылка на него. После выполнения второй строки кода переменную cat1 можно использовать так, как если бы она была объектом типа Cat. Обычно новый объект создается в одну строку (Cat cat1 = new Cat()).
|
||
Теперь немного подробнее рассмотрим оператор new.
|
||
Оператор new динамически выделяет память для нового объекта, общая форма применения этого оператора имеет следующий вид:
|
||
|
||
Имя_класса имя_переменной = new Имя_класса() //на самом деле не имя класса, а название конструктора;
|
||
|
||
|
||
Имя_класса() в правой части выполняет вызов конструктора данного класса, который позволяет подготовить наш объект к работе.
|
||
Возможна ситуация, когда две переменные указывают на один и тот же объект в памяти:
|
||
|
||
public static void main(String[] args) {
|
||
Cat cat1 = new Cat();
|
||
Cat cat2 = cat1;
|
||
|
||
cat1.name = "Барсик";
|
||
cat1.color = "Белый";
|
||
cat1.age = 4;
|
||
|
||
cat2.age = 5;
|
||
|
||
System.out.println("Кот 1 имя: " + cat1.name + " цвет: " + cat1.color + " возраст: " + cat1.age);
|
||
System.out.println("Кот 2 имя: " + cat2.name + " цвет: " + cat2.color + " возраст: " + cat2.age);
|
||
}
|
||
|
||
|
||
На первый взгляд может показаться, что переменной cat2 присваивается ссылка на копию объекта cat1, т.е. переменные cat1 и cat2 будут ссылаться на разные объекты в памяти. Но это не так. На самом деле cat1 и cat2 будут ссылаться на один и тот же объект. Присваивание переменной cat1 значения переменной cat2 не привело к выделению области памяти или копированию объекта, лишь к тому, что переменная cat2 ссылается на тот же объект, что и переменная cat1.
|
||
|
||
Таким образом, любые изменения, внесённые в объекте по ссылке cat2, окажут влияние на объект, на который ссылается переменная cat1, поскольку это один и тот же объект в памяти, как и в примере выше, где мы указали возраст второго кота 5 лет, а при выводе, возраст 5 лет оказался и у первого кота.
|
||
STATIC
|
||
Теперь мы знаем что такое класс и объект. На этом моменте хотелось бы остановиться на специальном модификаторе - static - с англ. "статичный", "постоянный" - делает переменную или метод "независимыми" от объекта. Ещё чуть подробнее, то: Static — модификатор, применяемый к полю, блоку, методу или внутреннему классу. Данный модификатор указывает на привязку субъекта к текущему классу.
|
||
В таком случае можно воспользоваться ключевым словом static, то есть объявить членов класса статическими. В Java большинство членов служебного класса являются статическими. Вот несколько примеров.
|
||
|
||
java.util.Objects содержит статические служебные операции для метода объекта;
|
||
java.util.Collections состоит исключительно из статических методов, которые работают с коллекциями или возвращают их.
|
||
|
||
Итак, где же можно употреблять данное ключевое слово?
|
||
Мы можем использовать это ключевое слово в четырех контекстах:
|
||
|
||
статические методы;
|
||
статические переменные;
|
||
статические вложенные классы;
|
||
статические блоки.
|
||
|
||
Рассмотрим подробнее каждый из перечисленных пунктов.
|
||
Статические методы
|
||
Статические методы также называются методами класса, потому что статический метод принадлежит классу, а не его объекту. Кроме того, статические методы можно вызывать напрямую через имя класса.
|
||
|
||
public class CalcExample {
|
||
public static void printSum(int a, int b) {
|
||
System.out.println(a + b);
|
||
}
|
||
|
||
public void printDifference(int a, int b) {
|
||
System.out.println(a - b);
|
||
}
|
||
|
||
public static void main(String[] args) {
|
||
/** Вызов статического метода **/
|
||
CalcExample.printSum(10, 2);
|
||
|
||
/** Вызов не-статического метода**/
|
||
CalcExample calcExample = new CalcExample();
|
||
CalcExample.printDifference(10, 2);
|
||
}
|
||
}
|
||
|
||
|
||
В приведенном выше примере метод printSum — статический, поэтому его можно вызывать напрямую с именем класса. Нет необходимости создавать новый экземпляр класса CalcExample. Но метод printDifference не является статическим. Таким образом, для нестатического метода необходимо создать новый экземпляр класса CalcExample.
|
||
Статические поля
|
||
При обозначении переменной уровня класса мы указываем на то, что это значение относится к классу. Если этого не делать, то значение переменной будет привязываться к объекту, созданному по этому классу.
|
||
Это значит, что если переменная не статическая, то у каждого нового объекта данного класса будет своё значение этой переменной, меняя которое мы меняем его исключительно в одном объекте:
|
||
Например, у нас есть класс котика с нестатической переменной:
|
||
|
||
public class Cat {
|
||
String name;
|
||
}
|
||
|
||
|
||
Тогда в мейн класс:
|
||
|
||
Cat cat1 = new Cat();
|
||
cat1.name = "Murka";
|
||
|
||
Cat cat2 = new Cat();
|
||
cat2.name = "Vasya";
|
||
|
||
System.out.println("First cat - " + cat1.name);
|
||
System.out.println("Second cat - " + cat2.name);
|
||
|
||
|
||
Вывод будет следующим:
|
||
|
||
First cat - Murka
|
||
Second cat - Vasya
|
||
|
||
|
||
Как видим, у каждого объекта своя переменная, изменение которой происходит только для этого объекта.
|
||
Ну а если у нас переменная статическая, то это глобальное значение — одно для всех:
|
||
Теперь мы имеем Cat со статической переменной:
|
||
|
||
public class Cat {
|
||
static String name;
|
||
}
|
||
|
||
|
||
По факту переменная у нас одна на всех, и каждый раз мы меняем именно ее.
|
||
К статическим переменным, как правило, обращаются не по ссылке на объект — cat1.name, а по имени класса — Cat.name
|
||
Соответственно, код из мейн класса после выполнения выведет следующее сообщение:
|
||
|
||
First cat - Vasya
|
||
Second cat - Vasya
|
||
|
||
|
||
К слову, статические переменные — редкость в Java. Вместо них применяют статические константы. Они определяются ключевым словом static final и представлены в верхнем регистре. Вот почему некоторые предпочитают использовать верхний регистр и для статических переменных.
|
||
Статические блоки
|
||
Статические блоки применяют для инициализации статических переменных. Статический блок выполняется только один раз, когда класс загружается в память. Это происходит, если в коде запрашивается либо объект класса, либо статические члены этого класса.
|
||
Ниже пример выведет имя котика:
|
||
|
||
public class Cat {
|
||
public static String name = null;
|
||
|
||
static {
|
||
name = "Murka";
|
||
}
|
||
|
||
public static void main(String[] args) {
|
||
System.out.println(name);
|
||
}
|
||
}
|
||
|
||
|
||
Класс может содержать несколько статических блоков, а каждый из них выполняется в той же последовательности, в которой они написаны в коде.
|
||
Последним контекстом использования ключевого слова static - это в части статических вложенных классов, которые мы рассмотрим чуть позднее.
|
||
|
||
\end{longtable}
|
||
|
||
\end{document}
|