Тема 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, у которого члены по умолчанию закрыты.
Конструкторы, деструкторы и статические члены
__init__ в Python). Конструкторов может быть несколько с разными параметрами — перегрузка.~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(): «каждая фигура умеет рисоваться, но как — знает только конкретная фигура».Каркас в предке, детали в наследниках:
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(), мы не меняем ни строчки в цикле рисования.
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, а не «к родителю» — поэтому в ромбе конструктор каждого класса вызывается ровно один раз.
Исключения в 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), который ждёт действий пользователя и вызывает привязанные обработчики. Библиотеки отличаются происхождением и лицензией:
| Библиотека | Основа | Особенности |
|---|---|---|
| Tkinter | Tk, входит в стандартную поставку Python | Ничего не нужно устанавливать; простые учебные и служебные интерфейсы; менеджеры компоновки pack / grid / place |
| PyQt / PySide | C++-фреймворк Qt | Самый богатый набор виджетов, визуальный редактор Qt Designer, механизм «сигналы–слоты» для связи событий с обработчиками; промышленные приложения |
| wxPython | wxWidgets | «Родной» внешний вид на каждой ОС |
| 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 и есть этот диспетчер: достаёт события из очереди и вызывает привязанные обработчики. Поэтому долгий расчёт внутри обработчика «замораживает» интерфейс — очередь событий не разбирается, окно не перерисовывается.
Вопросы к собеседованию
Как устроен класс в 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: правило = селектор { свойство: значение; … }. Три способа применить стили к документу:
- Внешняя таблица стилей —
<link rel="stylesheet" href="style.css">: один файл на весь сайт, кэшируется браузером — основной способ. - Внутренняя — правила в теге
<style>в head данной страницы. - Встроенный стиль — атрибут
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 | классы / атрибуты / псевдоклассы | теги | итог |
|---|---|---|---|---|
p | 0 | 0 | 1 | 0-0-1 |
div p.note | 0 | 1 | 2 | 0-1-2 |
#top .menu a:hover | 1 | 2 | 1 | 1-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.
Типичная ловушка: два блока по width:50% с padding в режиме content-box не помещаются в строку (50% + отступы > половины) — поэтому вёрстку обычно начинают с глобального box-sizing: border-box.
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)
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 говорит, где именно кликнули. Элементы, добавленные позже, работают автоматически — их не нужно подписывать отдельно.
Вопросы к собеседованию
Какова структура 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-запрос, сервер отвечает ресурсом или результатом работы программы. Динамические сайты используют трёхзвенную архитектуру:
- Клиент — браузер: отображение (HTML/CSS) и поведение (JavaScript);
- Сервер приложений — веб-сервер (Apache, nginx) + серверный язык (PHP): логика, проверка прав, формирование страниц;
- Сервер БД — СУБД (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 с идентификатором и сервер не удалил данные по тайм-ауту.
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 построен «слоёным пирогом» — каждый уровень пользуется службами нижнего:
- Ядро Linux — процессы, память, драйверы устройств, энергопитание; каждое приложение — отдельный Linux-пользователь в своей «песочнице».
- HAL и нативные библиотеки (C/C++) — доступ к камере, сенсорам, звуку; графика (OpenGL ES/Vulkan), SQLite, WebView.
- ART (Android Runtime) — среда выполнения: код на Java/Kotlin компилируется в байт-код, ART выполняет его с JIT/AOT-компиляцией; у каждого приложения — свой экземпляр виртуальной машины.
- Java API Framework — «кирпичи» для приложений: система окон и представлений (View), менеджеры активностей, уведомлений, пакетов, доступ к данным.
- Приложения — системные и установленные; распространяются пакетами 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.
Современная рекомендуемая архитектура (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 с переиспользованием ячеек.