Раздел 5 / 5

Прикладное программирование и веб-технологии

Тема 5.1

Объектно-ориентированное программирование

Понятие класса, поля и методы, ссылочные переменные, инкапсуляция, наследование, полиморфизм и динамическое связывание — вопросы билетов №6, №18.

Понятие класса и объекта

Класс — описание нового типа данных, объединяющее данные (поля, атрибуты) и операции над ними (методы) в единое целое. Класс — «чертёж», по которому создаются объекты.
Объект (экземпляр класса) — конкретная переменная типа «класс»: у каждого объекта свой набор значений полей, но общий для всех экземпляров набор методов.

Класс — чертёж дома: на нём указано, что у дома есть площадь и этажность (поля) и что в доме можно «открыть дверь» (метод). Объект — конкретный построенный дом: чертёж один, домов по нему — сколько угодно, и площадь у каждого своя.

Синтаксис и структура класса (на Java-подобном языке):

class Point {
    // поля (данные объекта)
    private double x;
    private double y;

    // конструктор — вызывается при создании экземпляра
    Point(double x, double y) { this.x = x; this.y = y; }

    // методы (операции над данными)
    double dist() { return Math.sqrt(x*x + y*y); }
    void moveTo(double nx, double ny) { x = nx; y = ny; }
}
  • Описание полей — как объявление переменных, но внутри класса; каждое поле имеет тип и модификатор доступа.
  • Описание методов — функции внутри класса; им доступны все поля «своего» объекта (через this / self).
  • Модификаторы доступа: public — виден всем; private — только внутри класса; protected — внутри класса и наследников.
  • Создание экземпляра: Point p = new Point(3, 4); — выделяется память в куче, вызывается конструктор.
  • Доступ к полям и вызов методов — через точку: p.dist(), p.moveTo(0, 0).

Чем класс отличается от структуры (записи)? Структура — только данные: набор полей, которые обрабатывают внешние функции. Класс добавляет к данным «поведение» (методы) и право прятать поля от чужого кода. Исторически class в C++ — это тот же struct, у которого члены по умолчанию закрыты.

Конструкторы, деструкторы и статические члены

Конструктор — специальный метод, автоматически вызываемый при создании объекта: приводит поля в согласованное начальное состояние и захватывает ресурсы. Имя совпадает с именем класса (Java, C++) или фиксировано (__init__ в Python). Конструкторов может быть несколько с разными параметрами — перегрузка.
Деструктор — метод, вызываемый при уничтожении объекта; освобождает захваченные ресурсы (память, файлы, соединения). В C++ вызывается детерминированно (~Point()), в Java и Python его роль берут на себя сборщик мусора и блоки finally / менеджеры контекста, потому что момент удаления объекта заранее неизвестен.
  • Перегрузка конструкторов: Point(), Point(x, y), Point(other) (копирующий) — нужный выбирается по числу и типам аргументов.
  • Статические (классовые) члены объявляются словом static и принадлежат классу, а не объекту: одно поле на все экземпляры (счётчик созданных объектов, общая настройка) и методы, вызываемые без объекта — Math.sqrt(). Статическому методу недоступен this.
  • Перегрузка методов (overloading) — несколько методов с одним именем и разными параметрами в одном классе; выбор — на этапе компиляции. Не путать с переопределением (overriding) — заменой унаследованного метода в наследнике с той же сигнатурой; выбор — во время выполнения.

Ссылочные переменные

В большинстве ООП-языков (Java, C#, Python, JavaScript) переменная типа «класс» хранит не сам объект, а ссылку на него: объект живёт в куче (heap), а переменная — лишь «адрес» этого объекта.

Два имени — один объект.

Point p1 = new Point(3, 4);
Point p2 = p1;      // копируется ССЫЛКА, а не объект!
p2.moveTo(0, 0);
// p1.dist() == 0 — p1 и p2 указывают на ОДИН объект

Следствия: присваивание и передача объекта в метод — дешёвые (копируется только адрес); сравнение p1 == p2 сравнивает адреса, а не содержимое (для содержимого — equals); переменная может быть «пустой» (null / None); объекты, на которые не осталось ссылок, уничтожает сборщик мусора.

Три принципа ООП

  • Инкапсуляция — объединение данных и методов в классе и сокрытие внутреннего устройства: поля закрыты (private), доступ — через методы (геттеры/сеттеры), которые могут проверять корректность. Внешний код зависит только от интерфейса класса — внутренности можно менять свободно.
  • Наследование — создание нового класса (производного, наследника) на основе существующего (базового): наследник получает все поля и методы родителя, добавляет свои и может переопределять унаследованные методы. Выражает отношение «является» (круг — это фигура).
  • Полиморфизм — возможность работать с объектами разных классов через общий интерфейс базового класса: один и тот же вызов fig.draw() выполняет разный код в зависимости от фактического класса объекта.

Часто к трём принципам добавляют четвёртый — абстракцию: в классе отражают только существенные для задачи свойства объекта, отбрасывая второстепенные. Класс Student в деканатской системе хранит ФИО и оценки, но не рост и цвет глаз; тот же студент в медицинской системе абстрагирован иначе. Инкапсуляция — техническое средство абстракции: несущественное прячется за интерфейсом.

Наследование в коде — базовый класс и наследник:

class Figure {
    protected double x, y;               // положение — общее для всех
    Figure(double x, double y) { this.x = x; this.y = y; }
    void move(double dx, double dy) { x += dx; y += dy; }
    double area() { return 0; }
}

class Circle extends Figure {            // Circle «является» Figure
    private double r;                    // добавили своё поле
    Circle(double x, double y, double r) {
        super(x, y);                     // вызов конструктора предка
        this.r = r;
    }
    @Override
    double area() { return Math.PI * r * r; }   // переопределили
}

Наследник вызывает конструктор предка (super), пользуется его методом move() без изменений и заменяет area() своей версией. Доступ к x, y возможен благодаря protected.

ОтношениеСмыслПример
Наследование («является», is-a)наследник — частный случай базового классаКруг — это Фигура
Композиция / агрегация («содержит», has-a)объект включает другие объекты как частиАвтомобиль содержит Двигатель

Типичная ошибка проектирования — наследовать там, где нужно включать: «Двигатель» не должен быть наследником «Автомобиля». Ориентир — принцип подстановки Лисков: наследование уместно, только если наследник везде может подменить базовый класс без сюрпризов.

Абстрактные классы и интерфейсы

Абстрактный класс — класс, объект которого создать нельзя; он служит общим предком: часть методов реализована, часть объявлена абстрактными (без тела) и обязана быть реализована в наследниках. Figure с абстрактным draw(): «каждая фигура умеет рисоваться, но как — знает только конкретная фигура».
Интерфейс — «чистый контракт»: только сигнатуры методов, без реализации и состояния. Класс может реализовать несколько интерфейсов — так языки с одиночным наследованием (Java, C#) получают безопасную замену множественного наследования классов.

Каркас в предке, детали в наследниках:

abstract class Figure {
    abstract double area();            // без тела — контракт
    void report() {                    // общий код для всех фигур
        System.out.println("S = " + area());   // вызов полиморфный!
    }
}
class Circle extends Figure {
    double r;
    double area() { return Math.PI * r * r; }
}

Метод report() написан один раз в предке, но печатает площадь конкретной фигуры: внутри него area() связывается поздно. Компилятор при этом гарантирует, что ни одна «недописанная» фигура без area() не будет создана.

Полиморфизм и динамическое связывание

Связывание — сопоставление вызова метода конкретному коду. Раннее (статическое) — на этапе компиляции, по типу переменной. Позднее (динамическое) — во время выполнения, по фактическому классу объекта, на который указывает ссылка.

Механизм: для класса с виртуальными методами компилятор строит таблицу виртуальных методов (vtable) — массив адресов реализаций. Каждый объект хранит указатель на таблицу своего класса. Вызов fig.draw() превращается в «взять из таблицы объекта адрес метода draw и перейти по нему» — поэтому один и тот же код обходит массив фигур, а рисуется каждая по-своему.

Figure[] figs = { new Circle(...), new Square(...), new Triangle(...) };
for (Figure f : figs)
    f.draw();   // тип переменной — Figure, но вызывается
                // Circle.draw / Square.draw / Triangle.draw

В Java методы виртуальны по умолчанию, в C++ нужно слово virtual, в Python всякий вызов метода — позднее связывание (поиск по имени в словаре класса). Полиморфное поведение методов — основа расширяемости: добавив класс Star с собственным draw(), мы не меняем ни строчки в цикле рисования.

демоПолиморфизм: один вызов draw() — разное поведение
Массив объявлен как Figure[], но в нём вперемешку круги, квадраты и треугольники. Цикл вызывает у всех один и тот же метод draw() — а таблица виртуальных методов у каждого объекта своя, поэтому каждая фигура рисует себя сама. Под фигурой подписан фактический класс, чей код выполнился.

Вопросы к собеседованию

Что такое класс и что такое объект?

Класс — описание нового типа данных, объединяющее поля (данные) и методы (операции над ними); это «чертёж». Объект (экземпляр) — конкретная переменная этого типа, созданная по чертежу: у каждого объекта свои значения полей, методы — общие для всех экземпляров класса.

Как описываются поля и методы класса и как к ним обращаться?

Поля объявляются внутри класса как переменные с типом и модификатором доступа; методы — как функции внутри класса, которым доступны поля своего объекта через this/self. Экземпляр создаётся оператором new (вызывается конструктор), доступ к полям и вызов методов — через точку: obj.field, obj.method(). Модификаторы: public — всем, private — только внутри класса, protected — классу и наследникам.

В чём особенность ссылочных переменных?

Переменная хранит не объект, а ссылку (адрес) на объект в куче. Присваивание копирует ссылку, поэтому две переменные могут указывать на один объект — изменение через одну видно через другую. Сравнение == сравнивает адреса, для сравнения содержимого нужен equals. Ссылка может быть пустой (null); объекты без ссылок удаляет сборщик мусора.

Что такое инкапсуляция и зачем она нужна?

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

Что даёт наследование?

Производный класс получает все поля и методы базового, добавляет свои и может переопределять унаследованные. Это повторное использование кода и выражение отношения «является» (круг — фигура): везде, где ожидается базовый класс, можно передать наследника. Иерархии наследования — основа полиморфизма.

Что такое полиморфизм и динамическое связывание?

Полиморфизм — работа с объектами разных классов через общий интерфейс базового: вызов fig.draw() выполняет код фактического класса объекта. Обеспечивается динамическим (поздним) связыванием: адрес метода выбирается во время выполнения по таблице виртуальных методов объекта, а не по типу переменной на этапе компиляции. Это позволяет добавлять новые классы, не меняя существующий код.

Чем раннее связывание отличается от позднего?

Раннее (статическое) — компилятор жёстко подставляет адрес метода по типу переменной; быстро, но поведение фиксировано. Позднее (динамическое) — метод выбирается во время выполнения по фактическому классу объекта (через vtable); чуть дороже, но даёт полиморфное поведение. В C++ позднее связывание включается словом virtual, в Java работает по умолчанию, в Python — всегда.

Тема 5.2

Python: классы и графический интерфейс

ООП в Python и библиотеки для настольных GUI-приложений — вопросы билетов №3, №12, №13.

ООП в Python

В Python всё — объект: числа, строки, функции, модули и сами классы. Класс объявляется словом class; первый параметр каждого метода — self, ссылка на текущий экземпляр (аналог this, но передаётся явно).

class Vector:
    dim = 2                          # атрибут КЛАССА (общий для всех)

    def __init__(self, x, y):        # конструктор
        self.x = x                   # атрибуты ЭКЗЕМПЛЯРА
        self.y = y

    def length(self):                # обычный метод
        return (self.x**2 + self.y**2) ** 0.5

    def __add__(self, other):        # перегрузка оператора +
        return Vector(self.x + other.x, self.y + other.y)

    def __str__(self):               # строковое представление
        return f"({self.x}; {self.y})"

v = Vector(3, 4) + Vector(1, 0)
print(v, v.length())                 # (4; 4) 5.656...
  • Атрибуты класса и экземпляра: dim один на всех, self.x — у каждого объекта свой. Атрибуты можно добавлять динамически.
  • Магические методы (__init__, __str__, __add__, __eq__, __len__…) встраивают класс в синтаксис языка: операторы, печать, сравнение, len().
  • Наследование: class Circle(Figure):; вызов родительской реализации — super().__init__(...). Поддерживается множественное наследование (порядок поиска — MRO).
  • Инкапсуляция — соглашением: «приватные» имена начинают с подчёркивания _x (или __x — имя искажается), настоящих модификаторов доступа нет. Контролируемый доступ — декоратор @property: снаружи это поле, внутри — метод с проверками.
  • Утиная типизация: полиморфизм без общего базового класса — подойдёт любой объект, у которого есть нужный метод («если крякает как утка — это утка»). Проверка типов происходит в момент вызова.

Полиморфизм по-питоновски — иерархия не обязательна:

class Circle:
    def draw(self): print("круг")

class Star:                # НЕ наследует Circle и вообще ничего
    def draw(self): print("звезда")

for f in [Circle(), Star()]:
    f.draw()               # работает: у обоих есть draw()

В Java такой цикл не скомпилируется без общего интерфейса; в Python достаточно совпадения «повадок» — это и есть утиная типизация.

Инкапсуляция, property и виды методов

Настоящих модификаторов доступа в Python нет — приватность держится на соглашениях и одном механизме языка:

  • _x — «внутреннее, не трогайте»: чистое соглашение, доступ не ограничен;
  • __x — искажение имени (name mangling): интерпретатор переименовывает атрибут в _ИмяКласса__x; случайный доступ и конфликт имён в наследниках исключены, намеренный — возможен;
  • @property — контролируемый доступ: снаружи атрибут, внутри метод с проверками.

property: снаружи поле — внутри проверка.

class Account:
    def __init__(self):
        self._balance = 0

    @property
    def balance(self):              # чтение: acc.balance
        return self._balance

    @balance.setter
    def balance(self, value):       # запись: acc.balance = 100
        if value < 0:
            raise ValueError("баланс не может быть отрицательным")
        self._balance = value

Код клиента не меняется при переходе от простого атрибута к проверяемому — в этом смысл property: инкапсуляция без геттеров-сеттеров в стиле Java.

Три вида методов в классе:

  • обычный метод получает self — работает с конкретным экземпляром;
  • классовый метод @classmethod получает cls — сам класс; типичное применение — альтернативные конструкторы: Date.from_string("17.08.2026");
  • статический метод @staticmethod не получает ни self, ни cls — обычная функция, положенная в класс «по смыслу».

Множественное наследование и MRO

Python разрешает несколько базовых классов: class C(A, B). Возникает вопрос «ромба»: если A и B оба наследуют X и переопределяют его метод — чью версию получит C? Ответ даёт MRO (method resolution order) — линейный порядок поиска методов, вычисляемый алгоритмом C3-линеаризации: каждый класс встречается один раз, наследники всегда раньше предков, порядок перечисления баз сохраняется.

«Ромб» и порядок поиска.

class X:
    def who(self): return "X"

class A(X):
    def who(self): return "A"

class B(X):
    def who(self): return "B"

class C(A, B):
    pass

C().who()      # "A"
C.__mro__      # (C, A, B, X, object)

Поиск идёт по цепочке C → A → B → X → object и останавливается на первом найденном имени. super() тоже ходит по MRO, а не «к родителю» — поэтому в ромбе конструктор каждого класса вызывается ровно один раз.

демоMRO: поиск метода в «ромбе» наследования
Ромб: C(A, B), а A и B наследуют X. Кнопка «Шаг поиска» идёт по C3-линеаризации C → A → B → X → object и останавливается на первом классе, где имя найдено. Если метод есть и в A, и в B — до X поиск не доходит: выигрывает A, потому что базы перечислены как (A, B). Каждый класс посещается один раз, наследники всегда раньше предков.

Исключения в Python

Ошибки времени выполнения — объекты-исключения; они «летят» вверх по стеку вызовов, пока их не перехватят. Обработка — блок try:

try:
    x = int(input("Введите число: "))
    print(100 / x)
except ValueError:
    print("это не число")
except ZeroDivisionError as e:   # объект исключения доступен как e
    print("деление на ноль:", e)
else:                            # выполняется, если исключений НЕ было
    print("успех")
finally:                         # выполняется всегда — очистка ресурсов
    print("конец попытки")
  • Исключения образуют иерархию классов (ValueError, TypeError, KeyError… — наследники Exception); except ловит указанный класс и всех его наследников.
  • Своё исключение — класс-наследник Exception; возбуждается оператором raise.
  • Для файлов и соединений вместо try/finally используют менеджер контекста: with open("f.txt") as f: — файл закроется сам даже при ошибке.
  • Идиома Python «проще попросить прощения, чем разрешения»: попытаться и перехватить ошибку вместо цепочки предварительных проверок — стиль, дополняющий утиную типизацию.

GUI-библиотеки Python

Все настольные GUI строятся по одной схеме: окно из виджетов (кнопки, поля, списки) + цикл обработки событий (event loop), который ждёт действий пользователя и вызывает привязанные обработчики. Библиотеки отличаются происхождением и лицензией:

БиблиотекаОсноваОсобенности
TkinterTk, входит в стандартную поставку PythonНичего не нужно устанавливать; простые учебные и служебные интерфейсы; менеджеры компоновки pack / grid / place
PyQt / PySideC++-фреймворк QtСамый богатый набор виджетов, визуальный редактор Qt Designer, механизм «сигналы–слоты» для связи событий с обработчиками; промышленные приложения
wxPythonwxWidgets«Родной» внешний вид на каждой ОС
Kivyсобственный движок (OpenGL)Мультитач, один код для настольных и мобильных платформ

Минимальное приложение на Tkinter — окно, поле, кнопка, обработчик:

import tkinter as tk

def on_click():
    lbl["text"] = "Привет, " + entry.get()

root = tk.Tk()                 # главное окно
entry = tk.Entry(root);  entry.pack()
tk.Button(root, text="OK", command=on_click).pack()
lbl = tk.Label(root);    lbl.pack()
root.mainloop()                # цикл обработки событий

Схема одна во всех библиотеках: создать виджеты → скомпоновать → привязать обработчики → запустить event loop. В PyQt та же связка называется «сигнал clicked подключён к слоту on_click».

Менеджеры компоновки Tkinter — три способа расставить виджеты в окне:

  • pack(side=...) — виджеты по очереди «прижимаются» к сторонам контейнера: быстро для простых форм;
  • grid(row=, column=) — таблица строк и столбцов: основной способ для форм «подпись — поле»;
  • place(x=, y=) — абсолютные координаты: полный контроль, но интерфейс не подстраивается под размер окна.

В одном контейнере нельзя смешивать pack и grid — менеджеры конфликтуют. Обычно окно делят на рамки (Frame), и внутри каждой работает свой менеджер.

Почему у GUI обязателен цикл событий? Консольная программа сама решает, когда читать ввод. Оконная — наоборот: она «спит», а операционная система будит её сообщениями «нажата кнопка», «перерисуй окно». mainloop и есть этот диспетчер: достаёт события из очереди и вызывает привязанные обработчики. Поэтому долгий расчёт внутри обработчика «замораживает» интерфейс — очередь событий не разбирается, окно не перерисовывается.

демоpack, grid, place: одна форма — три менеджера компоновки
Одни и те же пять виджетов формы входа. pack складывает их лентой сверху вниз — быстро, но подписи и поля не выровнены; grid строит таблицу «подпись — поле» — то, что нужно форме; place прибивает виджеты к абсолютным координатам — стройно ровно до первого изменения размера окна. Наведите указатель на виджет — в статусе появится вызов менеджера для него.

Вопросы к собеседованию

Как устроен класс в Python? Что такое self?

Класс объявляется словом class; конструктор — метод __init__. self — явная ссылка на текущий экземпляр, первый параметр каждого метода: через него методы читают и создают атрибуты объекта (self.x = x). При вызове obj.method() Python сам подставляет obj в self.

Чем атрибут класса отличается от атрибута экземпляра?

Атрибут класса объявлен в теле класса и общий для всех экземпляров (одно значение на класс); атрибут экземпляра создаётся через self (обычно в __init__) и у каждого объекта свой. При чтении Python ищет имя сначала в экземпляре, потом в классе, потом в базовых классах.

Как в Python работает наследование?

Базовые классы указываются в скобках: class Circle(Figure). Наследник получает все атрибуты и методы, может переопределять их; родительская реализация доступна через super() (обычно super().__init__() в конструкторе). Поддерживается множественное наследование, порядок поиска методов задаёт MRO (method resolution order).

Что такое магические методы? Приведите примеры.

Методы с двойными подчёркиваниями, которые Python вызывает неявно, встраивая объект в синтаксис языка: __init__ — создание, __str__ — печать, __add__ — оператор +, __eq__ — сравнение ==, __len__ — len(), __getitem__ — индексация []. Перегрузив их, свой класс делают «как встроенный тип».

Что такое утиная типизация?

Полиморфизм без обязательного общего базового класса: коду важно не «кем объект является», а «что он умеет» — подойдёт любой объект с нужным методом. Пригодность проверяется в момент вызова во время выполнения. Отсюда название: «если ходит как утка и крякает как утка — это утка».

Какие библиотеки для GUI на Python вы знаете?

Tkinter — стандартная (обёртка Tk), проста, менеджеры pack/grid/place. PyQt/PySide — обёртки Qt: богатые виджеты, Qt Designer, сигналы-слоты, промышленный уровень. wxPython — нативный вид на каждой ОС. Kivy — мультитач и мобильные платформы. Все строятся на цикле обработки событий и дереве виджетов.

Как устроено событийное GUI-приложение?

Программа создаёт окно и виджеты, привязывает к событиям обработчики (command в Tkinter, сигналы-слоты в Qt) и передаёт управление циклу событий (mainloop): он ждёт действий пользователя, превращает их в события и вызывает нужные обработчики. Логика приложения «живёт» в обработчиках, а не в линейном коде.

Тема 5.3

Веб-технологии: HTML, CSS, JavaScript

Теговая разметка и структура документа, применение CSS и селекторы, объектная модель документа — вопросы билетов №1, №17.

HTML: теговая разметка и структура документа

Принцип теговой разметки: содержимое документа размечается парными тегами <тег атрибут="значение">…</тег> (есть и одиночные: <img>, <br>, <input>). Теги задают структуру и смысл частей документа, а не внешний вид — вид определяет CSS. Теги могут вкладываться, образуя дерево.

<!DOCTYPE html>                  <!-- тип документа: HTML5 -->
<html lang="ru">
<head>                           <!-- служебная часть: не отображается -->
  <meta charset="utf-8">
  <title>Заголовок вкладки</title>
  <link rel="stylesheet" href="style.css">
</head>
<body>                           <!-- видимое содержимое -->
  <h1>Заголовок</h1>
  <p class="note">Абзац с <a href="…">ссылкой</a>.</p>
</body>
</html>
  • Типы html-документов задаёт декларация <!DOCTYPE>: современный HTML5 (<!DOCTYPE html>) против исторических HTML 4.01 и XHTML (строгий XML-синтаксис) с длинными DTD-декларациями. DOCTYPE переключает браузер в стандартный режим разбора.
  • Основные группы тегов: структурные (div, header, section), текстовые (h1–h6, p, ul/ol/li, table), гипертекст (a), медиа (img, video), формы (form, input, select, textarea).
  • Глобальные атрибуты id (уникальное имя элемента) и class (метка группы) — «крючки», за которые элемент цепляют CSS и JavaScript.

Семантические теги HTML5 размечают роли частей страницы: header (шапка), nav (навигация), main (основное содержимое), article (самостоятельный материал), section (смысловой раздел), aside (боковая колонка), footer (подвал). Для отображения это те же блоки, что и div, но смысловая разметка помогает поисковым системам и программам чтения с экрана понять структуру.

Формы — встроенный в HTML способ отправить данные на сервер:

<form action="/reg.php" method="post">
  <input type="text"     name="login" required minlength="3">
  <input type="password" name="pwd">
  <input type="email"    name="mail">    <!-- проверка формата почты -->
  <input type="number"   name="age" min="16" max="99">
  <select name="city">
    <option>Тамбов</option><option>Москва</option>
  </select>
  <textarea name="about"></textarea>
  <input type="submit" value="Отправить">
</form>

Атрибут name становится именем параметра на сервере; action — куда отправить, method — как (GET/POST). HTML5-атрибуты required, min/max, pattern и типы email, number, date дают проверку прямо в браузере, ещё до отправки.

CSS: способы применения и селекторы

Синтаксис CSS: правило = селектор { свойство: значение; … }. Три способа применить стили к документу:

  1. Внешняя таблица стилей — <link rel="stylesheet" href="style.css">: один файл на весь сайт, кэшируется браузером — основной способ.
  2. Внутренняя — правила в теге <style> в head данной страницы.
  3. Встроенный стиль — атрибут style="color:red" у конкретного элемента: самый высокий приоритет, но смешивает структуру с оформлением.

Виды селекторов:

СелекторПримерЧто выбирает
Универсальный*все элементы
По тегуpвсе абзацы
По классу.noteэлементы с class="note"
По идентификатору#topэлемент с id="top" (один)
По атрибутуinput[type="text"]элементы с данным атрибутом/значением
Потомокdiv pабзацы на любой глубине внутри div
Дочернийdiv > pабзацы — непосредственные дети div
Соседнийh2 + pабзац сразу после h2
Псевдоклассa:hover, li:first-childсостояние или позиция элемента
Псевдоэлементp::first-lineчасть элемента

Если элементу подходят несколько правил, побеждает более специфичный селектор (id > класс/атрибут/псевдокласс > тег), при равенстве — то, что объявлено позже (каскад). Часть свойств (шрифт, цвет) наследуется потомками.

Подсчёт специфичности — тройка чисел (id, классы, теги):

Селекторidклассы / атрибуты / псевдоклассытегиитог
p0010-0-1
div p.note0120-1-2
#top .menu a:hover1211-2-1

Сравнение поразрядное слева направо: 1-0-0 сильнее любого числа классов. Встроенный атрибут style сильнее любого селектора, а !important перебивает вообще всё — потому им и не злоупотребляют.

Блочная модель и раскладка

Каждый элемент для браузера — прямоугольник из четырёх зон: содержимое (content) → внутренние отступы (padding) → рамка (border) → внешние отступы (margin). По умолчанию итоговая ширина складывается из width + padding + border; режим box-sizing: border-box включает отступы и рамку внутрь заданной width — с ним вёрстка предсказуемее.

  • Блочные элементы (div, p, h1) занимают всю ширину и идут сверху вниз; строчные (span, a, b) текут внутри строки. Свойство display переключает поведение: block / inline / inline-block / none (скрыть).
  • Позиционирование position: static (обычный поток) · relative (сдвиг относительно своего места) · absolute (координаты от ближайшего позиционированного предка, из потока изъят) · fixed (относительно окна, не прокручивается) · sticky (прилипает при прокрутке).
  • Flexbox — современная раскладка рядов и колонок: контейнеру задают display:flex, элементы выстраиваются вдоль главной оси; justify-content распределяет их по главной оси, align-items — по поперечной. Отзывчивые интерфейсы без таблиц и обтеканий.

Экзаменационный расчёт ширины блока. Дано: width:200px; padding:20px; border:2px solid; margin:30px.

content-box: ширина блока = 200 + 2·20 + 2·2 = 244 px margin не входит место в строке = 244 + 2·30 = 304 px margin — внешний зазор border-box: ширина блока = width = 200 px content сжимается до 200 − 40 − 4 = 156 px

Типичная ловушка: два блока по width:50% с padding в режиме content-box не помещаются в строку (50% + отступы > половины) — поэтому вёрстку обычно начинают с глобального box-sizing: border-box.

демоБлочная модель: content → padding → border → margin
Ширина задана: width 180 px. Двигайте ползунки и следите за формулой в статусе: в режиме content-box каждый пиксель padding и border раздувает блок, в режиме border-box заданные 180 px удерживаются — сжимается содержимое. Пунктирная зона margin в ширину блока не входит — это внешний зазор до соседей.
демоТренажёр селекторов: кто попадёт под правило?
выберите селектор
Мини-документ показан рамками: каждая рамка — элемент с его тегом, классом или id. Нажмите селектор — подсветятся все элементы, которые он выбирает, а справа появится специфичность в виде (id, классы, теги). Сравните div p и div > p: потомок против непосредственного ребёнка.

Язык JavaScript: типы, функции, замыкания

JavaScript — интерпретируемый язык с динамической типизацией, исполняемый в браузере (движки V8, SpiderMonkey): тип принадлежит значению, а не переменной. Подключается тегом <script src="app.js"></script> или блоком <script>…</script>.

  • Типы данных: числа (единый тип number), строки, логический, undefined (значения нет), null (пусто намеренно), объекты {key: value}, массивы; функции — тоже значения.
  • Объявление переменных: let — изменяемая, блочная область видимости; const — константа (выбор по умолчанию); историческое var — функциональная область видимости и «всплытие» объявления, источник ошибок.
  • Сравнение: === — строгое, без преобразования типов; == — с приведением ("5" == 5 истинно!). Правило практики: всегда ===.
  • Функции — значения первого класса: их передают аргументами и возвращают из функций; стрелочная запись x => x * 2; методы массивов map / filter / forEach принимают функции-обработчики.
  • Замыкание — функция «помнит» переменные места, где была создана, даже после выхода из него: на этом держатся обработчики событий и сокрытие данных.
  • JSON — текстовый формат обмена «объект как литерал»: JSON.stringify(obj) ↔ JSON.parse(str); стандарт обмена клиента с сервером.
  • AJAX / fetch — запрос к серверу без перезагрузки страницы: fetch(url).then(r => r.json()).then(render); так работают «живые» интерфейсы — подсказки в поиске, бесконечные ленты.

Замыкание в три строки:

function makeCounter() {
    let n = 0;                 // локальная переменная...
    return () => ++n;          // ...живёт, пока жива функция
}
const next = makeCounter();
next(); next(); next();        // 1, 2, 3 — n сохраняется между вызовами

Переменная n недоступна извне — только через возвращённую функцию. Замыкание — это ещё и инкапсуляция без классов.

JavaScript и объектная модель документа (DOM)

DOM (Document Object Model) — представление html-документа в виде дерева объектов: каждый тег, атрибут и текст — узел дерева. Через глобальный объект document JavaScript читает и изменяет это дерево, и браузер немедленно перерисовывает страницу.
  • Отношения элементов: у узла есть родитель (parentNode), дети (children, firstElementChild, lastElementChild) и соседи (previousElementSibling / nextElementSibling) — по дереву можно ходить в любую сторону.
  • Методы поиска элементов: document.getElementById('top') — по id; getElementsByTagName, getElementsByClassName — живые коллекции; универсальные querySelector(css) / querySelectorAll(css) — по любому CSS-селектору.
  • Свойства элементов: innerHTML (разметка внутри), textContent (только текст), value (у полей форм), style (встроенные стили), classList (add / remove / toggle классов), атрибуты — getAttribute / setAttribute.
  • Создание и управление: document.createElement('li') → наполнить → вставить: parent.append(el), parent.prepend(el), ref.before(el) / ref.after(el); переместить — просто append в новое место (узел один, он переезжает); удалить — el.remove().
  • События: el.addEventListener('click', fn) — браузер вызовет fn при клике; события всплывают от элемента к корню, обработчик получает объект события.

Событие проходит три фазы: перехват (от корня вниз к цели), цель, всплытие (от цели вверх). Обработчики по умолчанию срабатывают на всплытии — поэтому один слушатель на родителе «слышит» клики по всем детям (делегирование событий). Остановить всплытие — event.stopPropagation(); отменить действие браузера по умолчанию (переход по ссылке, отправку формы) — event.preventDefault().

Делегирование: один обработчик на сотню строк списка.

const ul = document.querySelector('#list');
ul.addEventListener('click', e => {
    const li = e.target.closest('li');   // на каком li случился клик
    if (li) li.classList.toggle('done');
});

Обработчик один — на родителе; клики по любым li всплывают до ul, а e.target говорит, где именно кликнули. Элементы, добавленные позже, работают автоматически — их не нужно подписывать отдельно.

демоТри фазы события: перехват → цель → всплытие
Клик пришёлся на вложенную кнопку. Событие сначала спускается от корня к цели (фаза перехвата), срабатывает на самой цели, затем всплывает обратно — именно на всплытии работают обычные addEventListener. Включите stopPropagation() и прогоните ещё раз: обработчики предков перестают получать событие — а вместе с ними ломается и делегирование.
демоЖивое DOM-дерево: документ и его дерево синхронны
кликните элемент или кнопку
Слева — «страница» (вложенные рамки-элементы), справа — её DOM-дерево. Кликните любой элемент: подсветится он, его родитель (пунктиром) и дети. Кнопки выполняют настоящие DOM-операции над песочницей — смотрите, как документ и дерево меняются одновременно, а в статусе появляется выполненный код.

Вопросы к собеседованию

Какова структура html-документа?

Декларация <!DOCTYPE html> (тип документа), корневой <html>, внутри — <head> со служебной информацией (кодировка, title, подключение стилей и скриптов) и <body> с видимым содержимым. Содержимое размечается вложенными парными тегами и образует дерево элементов.

В чём принцип теговой разметки? Какие бывают типы документов?

Содержимое оборачивается тегами <тег атрибут="значение">…</тег>, которые описывают структуру и смысл (заголовок, абзац, таблица), а не внешний вид; вид задаёт CSS. Тип документа объявляет DOCTYPE: HTML5 — короткое <!DOCTYPE html>, исторические HTML 4.01 и XHTML (XML-синтаксис) — длинные DTD-декларации. DOCTYPE включает стандартный режим разбора браузера.

Какими способами CSS применяется к документу?

Три способа: внешняя таблица стилей через <link rel="stylesheet"> (основной — один файл на сайт, кэшируется); внутренняя — правила в теге <style> в head; встроенный стиль — атрибут style у элемента (самый высокий приоритет). Синтаксис правила: селектор { свойство: значение; }.

Перечислите виды селекторов CSS.

Универсальный *, по тегу (p), по классу (.note), по id (#top), по атрибуту (input[type="text"]); комбинаторы — потомок (div p), дочерний (div > p), соседний (h2 + p); псевдоклассы (:hover, :first-child) и псевдоэлементы (::first-line). При конфликте побеждает более специфичный селектор: id > класс > тег; при равенстве — последнее правило (каскад).

Что такое DOM?

Объектная модель документа: браузер разбирает HTML в дерево объектов-узлов (элементы, атрибуты, текст) и даёт JavaScript доступ к нему через объект document. Изменение дерева (добавление, удаление, правка узлов) немедленно отражается на странице. У каждого узла есть родитель, дети и соседи — по дереву можно перемещаться.

Какие есть методы поиска элементов в документе?

document.getElementById(id) — один элемент по id; getElementsByTagName / getElementsByClassName — живые коллекции по тегу или классу; querySelector(css) — первый элемент по любому CSS-селектору, querySelectorAll(css) — все совпадения. Современный код обычно использует querySelector/All как универсальные.

Как создать, вставить, переместить и удалить элемент?

Создание: document.createElement('li'), наполнение через textContent/innerHTML/setAttribute. Вставка: parent.append/prepend, ref.before/after. Перемещение — повторный append в новое место: узел существует в дереве один раз и переезжает. Удаление: el.remove(). Всё это сразу перерисовывается браузером.

Какие свойства элементов используются чаще всего?

innerHTML — разметка внутри элемента; textContent — только текст; value — содержимое полей форм; style — встроенные стили; classList с методами add/remove/toggle — управление классами; getAttribute/setAttribute — произвольные атрибуты; parentNode/children/nextElementSibling — навигация по дереву.

Как JavaScript реагирует на действия пользователя?

Через события: el.addEventListener('click', handler) регистрирует обработчик, браузер вызывает его при событии и передаёт объект события (координаты, клавиша, целевой элемент). События всплывают от целевого элемента вверх по дереву, что позволяет вешать один обработчик на общего родителя (делегирование).

Тема 5.4

Серверная сторона: PHP и MySQL

Обработка данных на сервере, передача данных форм, работа с СУБД — вопросы билетов №14, №15.

Архитектура веб-приложения: клиент–сервер

Веб построен на модели «клиент–сервер»: клиент (браузер) отправляет HTTP-запрос, сервер отвечает ресурсом или результатом работы программы. Динамические сайты используют трёхзвенную архитектуру:

  1. Клиент — браузер: отображение (HTML/CSS) и поведение (JavaScript);
  2. Сервер приложений — веб-сервер (Apache, nginx) + серверный язык (PHP): логика, проверка прав, формирование страниц;
  3. Сервер БД — СУБД (MySQL): хранение и выборка данных.

Разделение позволяет масштабировать и заменять звенья независимо: интерфейс можно переписать, не трогая базу. Важно: HTTP — протокол без состояния, каждый запрос самостоятелен, сервер «не помнит» клиента — состояние передают поверх протокола (cookie, сессии).

PHP: обработка данных на сервере

PHP — интерпретируемый язык, выполняющийся на сервере: браузер запрашивает страницу → веб-сервер передаёт файл интерпретатору PHP → тот выполняет код и формирует HTML → браузеру уходит уже готовая разметка (кода PHP клиент никогда не видит). Код встраивается в HTML вставками <?php … ?>.

  • Переменные начинаются с $, типизация динамическая: $x = 5; $s = "текст"; — объявления типов не нужны.
  • Строки: в двойных кавычках переменные подставляются ("Привет, $name"); конкатенация — точкой: $a . $b.
  • Массивы универсальны: индексные [1, 2, 3] и ассоциативные ["name" => "Иван"] — один тип данных; обход — foreach ($arr as $k => $v). Суперглобальные $_GET и $_POST — обычные ассоциативные массивы.
  • Функции: function f($a, $b = 1) { return $a + $b; }; управляющие конструкции — как в C: if/else, for, while, switch.

Методы передачи данных из формы на сервер:

  • GET — данные в строке адреса: search.php?q=SQL&page=2. Виден в адресе, можно сохранить в закладки, ограничен по длине. Для запросов, которые ничего не меняют (поиск, фильтры).
  • POST — данные в теле HTTP-запроса: не видны в адресе, размер практически не ограничен. Для изменения данных, паролей, загрузки файлов.

Суперглобальные массивы доступны в любом месте программы: $_GET и $_POST — данные соответствующего метода (ключ — имя поля формы), $_REQUEST — объединение обоих, $_FILES — загруженные файлы, $_SESSION и $_COOKIE — состояние между запросами, $_SERVER — сведения о запросе и сервере.

Форма и её обработка:

<form action="hello.php" method="post">
  <input type="text" name="user">
  <input type="submit" value="Отправить">
</form>

<?php                                  // hello.php
$name = $_POST['user'] ?? 'гость';     // данные поля user
echo "Привет, " . htmlspecialchars($name);
?>

Значение поля попадает в $_POST['user'] по атрибуту name. Перед выводом пользовательские данные пропускают через htmlspecialchars() — иначе присланный «текст» <script>…</script> выполнится у других посетителей (XSS-атака).

Состояние между запросами: cookie и сессии

Раз HTTP не хранит состояния, «залогиненность» и корзину покупателя реализуют поверх него:

  • Cookie — небольшие пары «имя-значение», которые сервер просит браузер хранить (setcookie()) и которые браузер автоматически прикладывает к каждому следующему запросу этому сайту; в PHP доступны через $_COOKIE. Хранятся у клиента, поэтому подделываемы — секретов там не держат.
  • Сессия — данные на сервере: session_start() выдаёт браузеру cookie со случайным идентификатором (PHPSESSID), а сами значения ($_SESSION['user']) лежат в файлах или памяти сервера. Клиент видит только идентификатор.

Типовая авторизация на сессиях:

<?php                       // login.php — проверка входа
session_start();
if (check_password($_POST['user'], $_POST['pwd'])) {
    $_SESSION['user'] = $_POST['user'];   // запомнили на сервере
}

// шапка любой защищённой страницы
session_start();
if (!isset($_SESSION['user'])) {
    header("Location: login.php");        // не авторизован — на вход
    exit;
}
?>

Выход из аккаунта — session_destroy(). Сессия живёт, пока браузер шлёт cookie с идентификатором и сервер не удалил данные по тайм-ауту.

демоGET, POST и сессия: диалог браузера с сервером
Слева — сообщения браузера, справа — ответы сервера. В GET-сценарии данные видны прямо в строке запроса и попадают в $_GET; адрес самодостаточен — работают закладки и «обновить». В POST-сценарии логин и пароль едут в теле запроса, сервер отвечает кукой PHPSESSID, и каждый следующий запрос браузер сам подписывает этой кукой — так поверх HTTP «без состояния» появляется «залогиненность».

PHP и MySQL

Для работы с СУБД в PHP есть расширение mysqli и универсальный интерфейс PDO (работает с разными СУБД). Схема всегда одна: соединиться → выполнить запрос → обработать результат → закрыть.

<?php
// 1. соединение с сервером БД: хост, пользователь, пароль, база
$db = new mysqli("localhost", "root", "secret", "userdata");

// 2а. добавление (модификация, удаление — так же)
$db->query("INSERT INTO users(name, city) VALUES('Иван', 'Тамбов')");
$db->query("UPDATE users SET city='Москва' WHERE id=3");
$db->query("DELETE FROM users WHERE id=7");

// 2б. выборка и обработка результатов
$res = $db->query("SELECT name, city FROM users");
while ($row = $res->fetch_assoc()) {          // строка → ассоц. массив
    echo $row['name'] . " — " . $row['city'] . "<br>";
}
$db->close();
?>

Подготовленные выражения обязательны, когда в запрос попадают данные пользователя: запрос с плейсхолдерами компилируется один раз, значения передаются отдельно и не могут изменить текст SQL — это защита от SQL-инъекций (ввод вида ' OR 1=1 -- в наивно склеенном запросе вернул бы всю таблицу).

$st = $db->prepare("SELECT * FROM users WHERE city = ?");
$st->bind_param("s", $_GET['city']);   // s — строковый параметр
$st->execute();
$res = $st->get_result();

mysqli или PDO? mysqli работает только с MySQL и существует в процедурном и объектном стиле. PDO — единый объектный интерфейс к разным СУБД (MySQL, PostgreSQL, SQLite…): смена базы — это смена строки подключения, а не переписывание кода. PDO поддерживает именованные плейсхолдеры и режим исключений при ошибках:

$pdo = new PDO("mysql:host=localhost;dbname=userdata", "root", "secret");
$st  = $pdo->prepare("SELECT * FROM users WHERE city = :city");
$st->execute(["city" => $_GET['city']]);
$rows = $st->fetchAll();

Сквозной мини-сценарий: таблица пользователей из БД.

<?php
$pdo = new PDO("mysql:host=localhost;dbname=userdata", "root", "secret");
$st  = $pdo->prepare("SELECT name, city FROM users WHERE city = :c");
$st->execute(["c" => $_GET['city'] ?? 'Тамбов']);

echo "<table>";
while ($row = $st->fetch()) {
    echo "<tr><td>" . htmlspecialchars($row['name']) .
         "</td><td>" . htmlspecialchars($row['city']) . "</td></tr>";
}
echo "</table>";
?>

Все три правила безопасности в одном экране: параметр запроса — через плейсхолдер (нет SQL-инъекции), вывод — через htmlspecialchars() (нет XSS), пароль БД живёт на сервере и клиенту не виден никогда.

Вопросы к собеседованию

Как PHP обрабатывает запрос? Чем серверный код отличается от клиентского?

PHP выполняется на сервере: веб-сервер передаёт файл интерпретатору, тот выполняет вставки <?php…?> и формирует готовый HTML, который отправляется браузеру — клиент кода не видит. JavaScript, напротив, исполняется в браузере у клиента. Серверный код имеет доступ к БД и файлам сервера; клиентский — к DOM страницы.

Чем метод GET отличается от POST?

GET передаёт данные в строке адреса (?q=…&page=…): виден, ограничен по длине, можно закладку — для запросов чтения. POST передаёт данные в теле HTTP-запроса: не виден в адресе, без практических ограничений размера, поддерживает файлы — для изменения данных и паролей. В PHP данные попадают в $_GET и $_POST соответственно.

Что такое суперглобальные массивы?

Встроенные массивы, доступные в любой области видимости: $_GET, $_POST — данные формы (ключ — атрибут name поля), $_REQUEST — их объединение, $_FILES — загруженные файлы, $_COOKIE и $_SESSION — состояние между запросами, $_SERVER — информация о запросе. Через них серверный сценарий получает всё, что прислал клиент.

Как из PHP работать с MySQL?

Через mysqli или PDO: установить соединение (хост, пользователь, пароль, база) — new mysqli(...); выполнять запросы query(): INSERT/UPDATE/DELETE для изменения, SELECT для выборки; результат выборки обходить в цикле fetch_assoc() (строка как ассоциативный массив по именам столбцов); закрыть соединение close().

Что такое SQL-инъекция и как от неё защищаются?

Атака, при которой пользовательский ввод, склеенный в текст запроса, меняет сам запрос (классика: ' OR 1=1 --). Защита — подготовленные выражения: запрос с плейсхолдерами (?) компилируется отдельно, значения привязываются bind_param и попадают в запрос только как данные. Дополнительно — проверка типов и минимальные права пользователя БД.

Зачем нужен htmlspecialchars при выводе данных?

Функция заменяет спецсимволы HTML (<, >, &, кавычки) на безопасные сущности. Без неё присланный пользователем «текст» со вставкой <script> выполнится в браузерах других посетителей (XSS-атака — межсайтовый скриптинг). Правило: всё, что пришло от пользователя, экранируется при выводе.

Тема 5.5

Архитектура мобильных приложений: Android

Слои платформы, компоненты приложения, жизненный цикл — вопрос билета №9.

Слои платформы Android

Android построен «слоёным пирогом» — каждый уровень пользуется службами нижнего:

  1. Ядро Linux — процессы, память, драйверы устройств, энергопитание; каждое приложение — отдельный Linux-пользователь в своей «песочнице».
  2. HAL и нативные библиотеки (C/C++) — доступ к камере, сенсорам, звуку; графика (OpenGL ES/Vulkan), SQLite, WebView.
  3. ART (Android Runtime) — среда выполнения: код на Java/Kotlin компилируется в байт-код, ART выполняет его с JIT/AOT-компиляцией; у каждого приложения — свой экземпляр виртуальной машины.
  4. Java API Framework — «кирпичи» для приложений: система окон и представлений (View), менеджеры активностей, уведомлений, пакетов, доступ к данным.
  5. Приложения — системные и установленные; распространяются пакетами APK.

Почему Java-код не исполняется «обычной» JVM? Мобильному устройству дороги память и батарея. Байт-код Java при сборке конвертируется в компактный формат DEX; исторический Dalvik интерпретировал его с JIT-компиляцией «на лету», современный ART добавил AOT-компиляцию — в машинный код при установке и в фоне: запуск быстрее, энергии тратится меньше. Сам APK-пакет — архив: DEX-код, ресурсы, нативные библиотеки и манифест, подписанные ключом разработчика.

Компоненты приложения и жизненный цикл

Android-приложение — не одна «программа с main()», а набор компонентов, которые система запускает по необходимости; все они объявляются в манифесте (AndroidManifest.xml) вместе с разрешениями:

  • Activity — один экран с интерфейсом; приложение — стопка активностей (навигация назад = снятие со стека);
  • Service — длительная работа без интерфейса (музыка, синхронизация в фоне);
  • Broadcast Receiver — реакция на широковещательные события (сеть появилась, батарея разряжена);
  • Content Provider — предоставление данных другим приложениям через единый интерфейс (контакты, галерея).

Компоненты связываются интентами (Intent) — «намерение» запустить экран или действие, своё или чужое («открыть камеру», «поделиться текстом»).

Явный и неявный Intent:

// явный — конкретный класс своего приложения
startActivity(new Intent(this, SettingsActivity.class));

// неявный — «действие», исполнителя подберёт система
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("text/plain");
i.putExtra(Intent.EXTRA_TEXT, "Привет!");
startActivity(i);                     // диалог «Поделиться»

Кто отзовётся на неявный интент, система решает по фильтрам интентов, объявленным в манифестах установленных приложений. Данные между экранами передают в «экстрах» интента (putExtra / getExtra).

Жизненный цикл Activity — цепочка обратных вызовов, которыми система сообщает экрану о смене состояния: onCreate (создание, построение интерфейса) → onStart (виден) → onResume (в фокусе, работает) → onPause (перекрыт) → onStop (не виден) → onDestroy (уничтожен). При повороте экрана активность пересоздаётся — поэтому состояние хранят вне её.

ВызовКогда происходитЧто обычно делают
onCreateактивность создаётсяsetContentView — загрузка разметки, инициализация
onStartэкран становится видимымобновить интерфейс свежими данными
onResumeэкран в фокусе, принимает вводзапустить анимации, камеру, датчики
onPauseфокус потерян: диалог, другой экраностановить камеру, сохранить черновик — быстро!
onStopэкран полностью не виденосвободить тяжёлые ресурсы
onRestartвозврат к экрану после onStopперед повторным onStart
onDestroyуничтожение (finish или система)финальная очистка

Вызовы парные: onCreate↔onDestroy, onStart↔onStop, onResume↔onPause — ресурс, занятый в одном, освобождают в парном. После onStop система вправе убить процесс ради памяти, поэтому важное сохраняют не позже onPause / onSaveInstanceState.

демоЖизненный цикл Activity: нажимайте, что делает пользователь
Лента — семь обратных вызовов Activity. Кнопки имитируют действия пользователя: система прогоняет экран по цепочке вызовов, и они подсвечиваются по очереди. Проверьте пары onCreate↔onDestroy, onStart↔onStop, onResume↔onPause; сравните «Диалог поверх» (только onPause) со «Свернуть» (onPause → onStop) и убедитесь, что поворот экрана — это полный снос и пересоздание активности.

Современная рекомендуемая архитектура (Jetpack): слой интерфейса — разметка/Compose + ViewModel (хранит состояние экрана, переживает пересоздание Activity) — слой данных — Repository, объединяющий локальную БД (Room/SQLite) и сеть. Данные текут к интерфейсу реактивно (LiveData/Flow), длинные списки рисует RecyclerView, переиспользующий ячейки. Схема близка к MVVM: View ↔ ViewModel ↔ Model.

Ресурсы, разметка и файлы проекта

Интерфейс и константы выносятся из кода в ресурсы — каталог res/ проекта:

  • res/layout/ — разметка экранов в XML: дерево контейнеров (LinearLayout, ConstraintLayout) и виджетов (TextView, Button, EditText). Активность загружает её в onCreate вызовом setContentView(R.layout.main), элементы находит findViewById(R.id.btn);
  • res/values/ — строки (strings.xml), цвета, размеры; локализация — просто второй файл строк в values-en/;
  • res/drawable/ — графика; квалификаторы ресурсов автоматически подставляют вариант под плотность экрана, язык и ориентацию.

На все ресурсы среда генерирует класс R с числовыми идентификаторами — обращения к ресурсам проверяются компилятором. Разделение кода и представления — та же идея, что HTML+CSS против «всего в коде».

Минимальная Activity: разметка + обработчик.

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);  // res/layout/activity_main.xml

        TextView label = findViewById(R.id.label);
        Button btn    = findViewById(R.id.btn);
        btn.setOnClickListener(v -> label.setText("Нажали!"));
    }
}

Всё узнаваемо по вебу: setContentView — как загрузка HTML-страницы, findViewById — как getElementById, setOnClickListener — как addEventListener. Разметка отдельно (XML в ресурсах), поведение отдельно (Java/Kotlin).

Вопросы к собеседованию

Из каких слоёв состоит платформа Android?

Снизу вверх: ядро Linux (процессы, память, драйверы, песочницы приложений); HAL и нативные библиотеки C/C++ (графика, SQLite); среда выполнения ART, исполняющая байт-код Java/Kotlin с JIT/AOT; Java API Framework (View, менеджеры системных служб); прикладной слой — приложения в пакетах APK.

Назовите четыре компонента Android-приложения.

Activity — экран с интерфейсом; Service — длительная фоновая работа без интерфейса; Broadcast Receiver — реакция на широковещательные события системы; Content Provider — предоставление данных другим приложениям. Все объявляются в манифесте; связываются и запускаются через Intent.

Опишите жизненный цикл Activity.

Система уведомляет экран обратными вызовами: onCreate (создание, инициализация интерфейса) → onStart (стал видим) → onResume (получил фокус) → onPause (частично перекрыт) → onStop (не виден) → onDestroy (уничтожение). При повороте экрана Activity пересоздаётся, поэтому состояние хранят во ViewModel или сохраняют в onSaveInstanceState.

Что такое Intent и манифест?

Intent — «намерение», объект-сообщение для запуска компонента или действия: явный (конкретный класс своего приложения) или неявный («открыть камеру», «поделиться» — система подберёт обработчика). AndroidManifest.xml — паспорт приложения: перечень всех компонентов, требуемые разрешения (камера, сеть), фильтры интентов.

Как устроена рекомендуемая архитектура Android-приложения?

Разделение на слои (близко к MVVM): интерфейс (View/Compose) наблюдает за ViewModel, которая хранит состояние экрана и переживает пересоздание Activity; данные даёт Repository, скрывающий источники — локальную БД (Room) и сеть. Поток данных реактивный (LiveData/Flow); списки — RecyclerView с переиспользованием ячеек.