Перейти к содержимому

Super java что это

  • автор:

Наследование

Наследование является неотъемлемой частью Java. При использовании наследования вы говорите: Этот новый класс похож на тот старый класс. В коде это пишется как extends, после которого указываете имя базового класса. Тем самым вы получаете доступ ко всем полям и методам базового класса. Используя наследование, можно создать общий класс, которые определяет характеристики, общие для набора связанных элементов. Затем вы можете наследоваться от него и создать новый класс, который будет иметь свои уникальные характеристики. Главный наследуемый класс в Java называют суперклассом. Наследующий класс называют подклассом. Получается, что подкласс — это специализированная версия суперкласса, которая наследует все члены суперкласса и добавляет свои собственные уникальные элементы. К примеру, в Android есть класс View и подкласс TextView.

Чтобы наследовать класс, достаточно вставить имя наследуемого класса с использованием ключевого слова extends:

 public class MainActivity extends Activity

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

Подкласс в свою очередь может быть суперклассом другого подкласса. Так например, упоминавший ранее класс TextView является суперклассом для EditText.

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

Для каждого создаваемого подкласса можно указывать только один суперкласс. При этом никакой класс не может быть собственным суперклассом.

Хотя подкласс включает в себя все члены своего суперкласса, он не может получить доступ к тем членам суперкласса, которые объявлены как private.

Помните, мы создавали класс Box для коробки кота. Давайте наследуемся от этого класса и создадим новый класс, который будет иметь не только размеры коробки, но и вес.

В том же файле Box.java после последней закрывающей скобки добавьте новый код:

 class HeavyBox extends Box < int weight; // вес коробки // конструктор HeavyBox(int w, int h, int d, int m) < width = w; height = h; depth = d; weight = m; // масса >> 

Возвращаемся в главную активность и пишем код:

 HeavyBox box = new HeavyBox(15, 10, 20, 5); int vol = box.getVolume(); mInfoTextView.setText("Объём коробки: " + vol + " Вес коробки: " + box.weight); 

Обратите внимание, что мы вызываем метод getVolume(), который не прописывали в классе HeavyBox. Однако мы можем его использовать, так как мы наследовались от класса Box и нам доступны все открытые поля и методы. Заодно мы вычисляем вес коробки с помощью новой переменной, которую добавили в подкласс.

Теперь у нас появилась возможность складывать в коробку различные вещи. В хозяйстве всё пригодится.

При желании вы можете создать множество разных классов на основе одного суперкласса. Например, мы можем создать цветную коробку.

 class ColorBox extends Box < int color; // цвет коробки // конструктор ColorBox(int w, int h, int d, int c) < width = w; height = h; depth = d; color = c; // цвет >> 

Ключевое слово super

В Java существует ключевое слово super, которое обозначает суперкласс, т.е. класс, производным от которого является текущий класс. В данном случае, супер не означает превосходство, скорее даже наоборот, дочерний класс имеет больше методов, чем родительский. Само слово пошло из теории множеств, где используется термин супермножество. Посмотрим, зачем это нужно.

В конструкторе HeavyBox мы дублировали поля width,height и depth, которые уже есть в классе Box. Это не слишком эффективно. Кроме того, возможны ситуации, когда суперкласс имеет закрытые члены данных, но мы хотим иметь к ним доступ. Через наследование это не получится, так как закрытые члены класса доступны только родному классу. В таких случаях вы можете сослаться на суперкласс.

Ключевое слово super можно использовать для вызова конструктора суперкласса и для обращения к члену суперкласса, скрытому членом подкласса.

Использование ключевого слова super для вызова конструктора суперкласса

 class HeavyBox extends Box < int weight; // вес коробки // конструктор // инициализируем переменные с помощью ключевого слова super HeavyBox(int w, int h, int d, int m) < super(w, h, d); // вызов конструктора суперкласса weight = m; // масса >> 

Вызов метода super() всегда должен быть первым оператором, выполняемым внутри конструктора подкласса.

При вызове метода super() с нужными аргументами, мы фактически вызываем конструктор Box, который инициализирует переменные width, height и depth, используя переданные ему значения соответствующих параметров. Вам остаётся инициализировать только своё добавленное значение weight. При необходимости вы можете сделать теперь переменные класса Box закрытыми. Проставьте у полей класса Box модификатор private и убедитесь, что вы можете обращаться к ним без проблем.

У суперкласса могут быть несколько перегруженных версий конструкторов, поэтому можно вызывать метод super() с разными параметрами. Программа выполнит тот конструктор, который соответствует указанным аргументам.

Вторая форма ключевого слова super действует подобно ключевому слову this, только при этом мы всегда ссылаемся на суперкласс подкласса, в котором она использована. Общая форма имеет следующий вид:

super.член

Здесь член может быть методом либо переменной экземпляра.

Подобная форма подходит в тех случаях, когда имена членов подкласса скрывают члены суперкласса с такими же именами.

 class A < int i; >// наследуемся от класса A class B extends A < int i; // имя переменной совпадает и скрывает переменную i в классе A B(int a, int b) < super.i = a; // обращаемся к переменной i из класса A i = b; // обращаемся к переменной i из класса B >void show() < System.out.println("i из суперкласса: " + super.i); System.out.println("i в подклассе: " + i); >> class MainActivity

В результате мы должны увидеть:

i из суперкласса: 1 i в подклассе: 2

Таким образом, знакомое нам выражение super.onCreate(savedInstanceState) обращается к методу onCreate() из базового класса.

Создание многоуровневой иерархии

Мы использовали простые примеры, состоящие из суперкласса и подкласса. Можно строить более сложные конструкции, содержащие любое количество уровней наследования. Например, класс C может быть подклассом класса B, который в свою очередь является подклассом класса A. В подобных ситуациях каждый подкласс наследует все характеристики всех его суперклассов.

Напишем пример из трёх классов. Суперкласс Box, подкласс HeavyBox и подкласс MoneyBox. Последний класс наследует все характеристики классов Box и HeavyBox, а также добавляет поле cost, которое содержит стоимость коробки.

Box.java

 package ru.alexanderklimov.expresscourse; class Box < private int width; // ширина коробки private int height; // высота коробки private int depth; // глубина коробки // Конструктор для создания клона объекта Box(Box ob) < // передача объекта конструктору width = ob.width; height = ob.height; depth = ob.depth; >// Конструктор, используемый при указании всех измерений Box(int w, int h, int d) < width = w; height = h; depth = d; >// Конструктор, используемый, когда ни одно из измерений не указано Box() < // значение -1 используется // для указания неинициализированного параллелепипеда width = -1; height = -1; depth = -1; >// Конструктор для создания куба Box(int len) < width = height = depth = len; >// вычисляем объём коробки int getVolume() < return width * height * depth; >> 

HeavyBox.java

 package ru.alexanderklimov.expresscourse; //Добавление веса class HeavyBox extends Box < int weight; // вес коробки // Конструктор клона объекта HeavyBox(HeavyBox ob) < // передача объекта конструктору super(ob); weight = ob.weight; >// Конструктор, используемый // при указании всех параметров HeavyBox(int w, int h, int d, int m) < super(w, h, d); // вызов конструктора суперкласса weight = m; // масса >// Конструктор по умолчанию HeavyBox() < super(); weight = -1; >// Конструктор для создания куба HeavyBox(int len, int m) < super(len); weight = m; >> 

MoneyBox

 package ru.alexanderklimov.expresscourse; //Цена коробки class MoneyBox extends HeavyBox < int cost; // Конструирование клона объекта MoneyBox(MoneyBox ob) < // передача объекта конструктору super(ob); cost = ob.cost; >// Конструктор, используемый // при указании всех параметров MoneyBox(int w, int h, int d, int m, int c) < super(w, h, d, m); // вызов конструктора суперкласса cost = c; >// Конструктор по умолчанию MoneyBox() < super(); cost = -1; >// Конструктор для создания куба MoneyBox(int len, int m, int c) < super(len, m); cost = c; >> 

Код для основной активности, например, при щелчке кнопки:

 public void onClick(View v)

В результате мы получим различные значения, вычисляемые в коде. Благодаря наследованию, класс MoneyBox может использовать классы Box и HeavyBox, добавляя только ту информацию, которая нам требуется для его собственного специализированного применения. В этом и состоит принцип наследования, позволяя повторно использовать код.

Метод super() всегда ссылается на конструктор ближайшего суперкласса в иерархии. Т.е. метод super() в классе MoneyBox вызывает конструктор класса HeavyBox, а метод super() в классе HeavyBox вызывает конструктор класса Box.

Если в иерархии классов конструктор суперкласса требует передачи ему параметров, все подклассы должны передавать эти параметры по эстафете.

В иерархии классов конструкторы вызываются в порядке наследования, начиная с суперкласса и заканчивая подклассом. Если метод super() не применяется, программа использует конструктор каждого суперкласса, заданный по умолчанию или не содержащий параметров.

Вы можете создать три класса A, B, C, которые наследуются друг от друга (A←B←C), у которых в конструкторе выводится текст и вызвать в основном классе код:

 C c = new C(); 

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

Переопределение методов

Если в иерархии классов имя и сигнатура типа метода подкласса совпадает с атрибутами метода суперкласса, то метод подкласса переопределяет метод суперкласса. Когда переопределённый метод вызывается из своего подкласса, он всегда будет ссылаться на версию этого метода, определённую подклассом. А версия метода из суперкласса будет скрыта.

Если нужно получить доступ к версии переопределённого метода, определённого в суперклассе, то используйте ключевое слово super.

Не путайте переопределение с перегрузкой. Переопределение метода выполняется только в том случае, если имена и сигнатуры типов двух методов идентичны. В противном случае два метода являются просто перегруженными.

В Java SE5 появилась запись @Override; она не является ключевым словом. Если вы собираетесь переопределить метод, используйте @Override, и компилятор выдаст сообщение об ошибке, если вместо переопределения будет случайно выполнена перегрузка.

Для закрепления материала создадим класс Animal с одним методом.

 package ru.alexanderklimov.expresscourse; public class Animal < String sleep()< return "Животные иногда спят"; >> 

Теперь создадим класс Cat, наследующий от первого класса.

 package ru.alexanderklimov.expresscourse; public class Cat extends Animal

Java знает, у родительского класса есть метод sleep(). Удостовериться можно следующим образом. Находясь в классе Cat, выберите в меню Source | Override/Implement Methods. . Появится диалоговое окно, где можно отметить флажком нужный метод.

Extends

В результате в класс будет добавлена заготовка:

 @Override String sleep() < // TODO Auto-generated method stub return super.sleep(); >

Попробуем вызвать данный метод в основном классе активности:

 public void onClick(View v)

Мы получим текст, который определён в суперклассе, хотя вызывали метод дочернего класса.

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

 @Override String sleep() < //return super.sleep(); return "Коты постоянно спят!"; >

Запускаем программу и нажимаем на кнопку. И получим уже другой ответ, более соответствующий описанию среднестатистического кота. Заметьте, что код для щелчка кнопки мы не меняем, но система сама разберётся, что выводить нужно текст не из суперкласса, а из дочернего класса.

Рассмотрим другой пример переопределения методов. Создадим суперкласс Figure, который будет содержать размеры фигуры, а также метод для вычисления площади. А затем создадим два других класса Rectangle и Triangle, у которых мы переопределим данный метод.

 class Figure < double dim1; double dim2; Figure(double a, double b) < dim1 = a; dim2 = b; >double area() < System.out.printLn("Площадь фигуры"); return 0; >> class Rectangle extends Figure < Rectangle(double a, double b) < super(a, b); >// Переопределяем метод double area() < System.out.println("Площадь прямоугольника"); return dim1 * dim2; >> class Triangle extends Figure < Triangle(double a, double b) < super(a, b); >// переопределяем метод double area() < System.out.println("Площадь треугольника"); return dim1 * dim2 / 2; >> // В главной активности Figure figure = new Figure(10, 10); Rectangle rectangle = new Rectangle(8, 5); Triangle triangle = new Triangle(10, 6); Figure fig; fig = figure; mInfoTextView.setText("Площадь равна " + fig.area); fig = rectangle; mInfoTextView.setText("Площадь равна " + fig.area); fig = triangle; mInfoTextView.setText("Площадь равна " + fig.area); 

Как видите, во всех классах используется одно и тоже имя метода, но каждый класс по своему вычисляет площадь в зависимости от фигуры. Это очень удобно и позволяет не придумывать новые названия методов в классах, которые наследуются от базового класса.

Ключевое слово super

Ключевое слово super в Java используется тогда, когда подклассу требуется сослаться на его непосредственный супер класс.

У ключевого слова super имеются две общие формы:

    Для вызова конструктора супер класса:

super(списокАргументов);​
suреr.member;​

2. Вызов конструкторов супер класса с помощью ключевого слова super

Если в иерархии классов требуется передать параметры конструктору супер класса, то все подклассы должны передавать эти параметры вверх по иерархии. То есть из конструктора подкласса надо вызвать конструктор супер класса с помощью super() . Когда метод super() вызывается из подкласса, вызывается конструктор его непосредственного супер класса. Это справедливо даже для многоуровневой иерархии.

Вызов метода super() должен быть всегда в первом операторе, выполняемом в теле конструктора подкласса.

В этом примере из конструктора класса HeavyBox1 вызываем конструктора класса Box6 c помощью super() , тем самым передавая необходимые значения:

public class Box6 < double width; double height; double depth; Box6(double w, double h, double d) < width = w; height = h; depth = d; >public Box6() < >>
public class HeavyBox1 extends Box6 < int weight; public HeavyBox1(int width, int height, int depth, int weight) < super(width, height, depth); this.weight = weight; >public HeavyBox1() < this.weight = -1; >>

Если в конструкторе наследника нет явного вызова super() , как например во втором конструкторе класса HeavyBox1 , JVM сама его подставляет первой строкой:

public HeavyBox1()

Из этого следует, что супер класс должен иметь конструктор без параметров, иначе возникнет ошибка компиляции.

3. Обращения к члену супер класса с помощью ключевого слова super

С помощью ключевого слова super можно обратиться к члену супер класса из класса наследника. Чаще всего это можно сделать не используя super , но в этом примере рассмотрим случаи, когда без него не обойтись.

В классе С объявлена переменная i типа int . В его наследнике классе D , тоже объявлена переменная i , но типа String . (Сразу же предупредим — на практике не стоит так делать! Пример приводится с целью иллюстрирования применение ключевого слова super с переменными.) Из класса D мы можем напрямую обратиться только к переменной String i , которая перекрывает область видимости переменной int i . Для обращения же к int i , необходимо использовать слово super .

Похожая ситуация и с методами. В обоих классах определен метод print() . Если мы хотим из класса D вызвать метод print() класса С , используем слово super super.print() .

public class C < public int i; public void print() < System.out.println("C.i = " + i); >> public class D extends C < public String i; public D(String a, int b) < i = a; super.i = b; >public void print() < System.out.println("D.i = " + i); super.print(); >> public class UseSuper < public static void main(String[] args) < D d = new D("someString", 2); d.print(); System.out.println(d.i); >>

Вывод на консоль:

D.i = someString C.i = 2 someString

Ключевое слово super, оператор instanceof

На предыдущем занятии мы с вами рассмотрели основу механизма наследования. Здесь затронем некоторые важные нюансы этого процесса и начнем с рассмотрения ключевого слова super.

Ключевое слово super

Часто в базовых классах имеются несколько конструкторов. Как в этом случае узнать, какой из конструкторов будет вызван при создании объекта дочернего класса? Например, объявим два класса:

class Properties { int width, color; Properties() { System.out.println("Конструктор Properties()"); } Properties(int width, int color) { this.width = width; this.color = color; System.out.println("Конструктор Properties(width, color)"); } } class Line extends Properties { double x1, y1; double x2, y2; Line() { System.out.println("Конструктор Line()"); } }

Здесь в базовом классе Properties два конструктора. Если теперь выполнить создание объекта дочернего класса Line:

Line line = new Line();

то в консоли увидим строчки:

Конструктор Properties()
Конструктор Line()

Это говорит о том, что при создании объекта базового класса, вызывается или конструктор без аргументов, или конструктор по умолчанию. Причем, конструктор по умолчанию будет существовать, только если в классе не объявлены никакие другие конструкторы. То есть, если конструктор Properties() закомментировать и оставить только второй, то объект дочернего класса Line не будет создан, возникнет ошибка, т.к. отсутствует конструктор без аргументов. А вот если в комментарии поставить и второй конструктор, то все заработает благодаря появлению конструктора по умолчанию.

Но, что если мы хотим воспользоваться вторым конструктором базового класса с двумя аргументами? В этом случае, при вызове конструктора дочернего класса нужно явно указать какой конструктор базового класса следует использовать. Это делается с помощью ключевого слова super, следующим образом:

class Line extends Properties { double x1, y1; double x2, y2; Line() { super(0, 0); System.out.println("Конструктор Line()"); } }

Здесь в конструкторе Line первой строчкой идет вызов конструктора базового класса с двумя аргументами. Обратите внимание, вызывать конструктор базового класса следует сразу же в первой строчке, т.к. сначала должен быть создан объект базового класса, а затем уже, объект дочернего класса.

При запуске программы увидим две строчки:

Конструктор Properties(width, color)
Конструктор Line()

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

Ключевое слово super в роли ссылки

После создания объекта базового класса, ключевое слово super можно использовать как ссылку на этот объект. Например, определим в базовом классе целочисленное поле id со значением 1:

int id = 1;

и точно такое же поле в дочернем классе, но со значением 2:

int id = 2;

Как теперь из дочернего класса обратиться к id базового класса? Да, это можно сделать через ссылку super:

class Line extends Properties { int id = 2; . void showId() { System.out.println("id = "+ id + ", super.id = " + super.id); } }

При вызове этого метода, в консоли увидим:

id = 2, super.id = 1

Также, если определить метод showId() в базовом классе:

class Properties { int id = 1; . void showId() { System.out.println("id = "+ id); } }

то мы можем к нему обратиться из дочернего через ссылку super:

class Line extends Properties { int id = 2; . void showId() { super.showId(); System.out.println("id = "+ id + ", super.id = " + super.id); } }

Вот так можно вызывать переопределенные поля и методы из объекта базового класса, используя ссылку super.

Оператор instanceof

В действительности в Java любой класс автоматически наследуется от предопределенного класса Object. То есть, иерархию наследования в нашем примере следовало бы изобразить вот так:

И объекты дочерних классов можно было бы создавать, используя ссылку типа Object:

Object g1 = new Line(); Object g2 = new Triangle(); Object g3 = new Properties();

При этом тип данных для ссылок g1, g2, g3 будет автоматически приведен к типу Object, т.к. это базовый суперкласс для всех этих объектов. Это, так называемое, восходящее преобразование (по-английски upcasting). Но мы можем выполнять и нисходящие преобразования: от ссылок на базовые классы к ссылкам на дочерние классы. Такое обратное преобразование типов автоматически уже не происходит и мы должны явно указать, какой тип дочернего класса нам нужен. Например, так:

Line l1 = (Line)g1; Line l2 = (Line)g2; // ошибка - это класс Triangle Properties p1 = (Properties)g3;

И смотрите, что может произойти. Когда мы пытаемся ссылку g2 привести к типу Line, то в процессе выполнения программы возникнет исключение (ошибка), что класс Triangle не может быть приведен к классу Line. И это логично, т.к. оба класса – самостоятельные дочерние, унаследованные от Properties. Нельзя один подменить другим. Но, хорошо, мы в этой простой программе знаем, что g2 – это ссылка на Triangle. А как быть в сложных проектах, когда имеется обобщенная ссылка и нам важно знать, какие дочерние классы включены в объект, на который ссылается g2? Для таких случаев существует оператор

который возвращает true, если имя дочернего класса содержится в объекте, по указанной ссылке. Например, прежде чем выполнять нисходящее преобразование типов, было бы правильно проверить, а содержится ли объект дочернего класса в объекте, к которому мы хотим привести тип данных:

Line l1 = null, l2 = null; if(g1 instanceof Line) l1 = (Line)g1; if(g2 instanceof Line) l2 = (Line)g2; // это класс Triangle Properties p1 = null; if(g3 instanceof Properties) p1 = (Properties)g3;

Теперь, при запуске программы у нас никаких ошибок не будет, т.к. проверка g2 instanceof Line будет ложной и преобразование не выполняется.

Путь кодера

  • displayPen() – для отображения данных по ручкам;
  • displayPencil() – для отображения данных по карандашам;
  • displayNotebook() – для отображения данных по тетрадям.

super

Ключевое слово super используется для вызова функций, принадлежащих родителю объекта.

Выражения super.prop и super[expr] действительны в любом определении метода в классах и в литералах объекта.

Синтаксис

super([arguments]); // вызов родительского конструктора. super.functionOnParent([arguments]); 

Описание

В конструкторе ключевое слово super() используется как функция, вызывающая родительский конструктор. Её необходимо вызвать до первого обращения к ключевому слову this в теле конструктора. Ключевое слово super также может быть использовано для вызова функций родительского объекта.

Пример

Использование super в классах

Этот фрагмент кода взят из classes sample (демонстрация). В этом примере super() вызывается, чтобы не повторять части конструктора, одинаковые для классов Rectangle и Square .

class Rectangle  constructor(height, width)  this.name = "Rectangle"; this.height = height; this.width = width; > sayName()  console.log("Hi, I am a ", this.name + "."); > get area()  return this.height * this.width; > set area(value)  this._area = value; > > class Square extends Rectangle  constructor(length)  this.height; // ReferenceError, super должен быть вызван первым! // Здесь вызывается конструктор родительского класса с длинами, // указанными для ширины и высоты класса Rectangle super(length, length); // Примечание: в производных классах super() необходимо вызывать, прежде чем // использовать 'this'. Если этого не сделать, произойдет ошибка ReferenceError. this.name = "Square"; > > 

Вызов статических методов через super

Вы также можете вызывать super для статических методов.

class Rectangle  static logNbSides()  return "У меня 4 стороны"; > > class Square extends Rectangle  static logDescription()  return super.logNbSides() + ", равные между собой"; > > Square.logDescription(); // 'У меня 4 стороны, равные между собой' 

Удаление свойств через super вызывает ошибку

Вы не можете использовать оператор delete и super.prop или super[expr] для удаления свойств родительского класса, он выдаст: ReferenceError .

class Base  constructor() > foo() > > class Derived extends Base  constructor() > delete()  delete super.foo; // это плохо > > new Derived().delete(); // ReferenceError: invalid delete involving 'super'. 

super.prop не может переопределять свойства, защищённые от записи

При определении незаписываемых свойств с помощью, например, Object.defineProperty , super не может перезаписать значение свойства.

class X  constructor()  Object.defineProperty(this, "prop",  configurable: true, writable: false, value: 1, >); > > class Y extends X  constructor()  super(); > foo()  super.prop = 2; // Невозможно перезаписать значение. > > var y = new Y(); y.foo(); // TypeError: "prop" доступен только для чтения console.log(y.prop); // 1 

Использование super.prop в объектных литералах

Super также можно использовать в объекте инициализатора / литерала. В этом примере метод определяют два объекта. Во втором объекте super вызывает метод первого объекта. Это работает благодаря Object.setPrototypeOf() , с помощью которого мы можем установить прототип для obj2 в obj1 , так что super может найти method1 в obj1 .

var obj1 =  method1()  console.log("method 1"); >, >; var obj2 =  method2()  super.method1(); >, >; Object.setPrototypeOf(obj2, obj1); obj2.method2(); // выведет "method 1" 

Спецификации

Specification
ECMAScript Language Specification
# sec-super-keyword

Совместимость с браузерами

BCD tables only load in the browser

Смотрите также

Found a content problem with this page?

  • Edit the page on GitHub.
  • Report the content issue.
  • View the source on GitHub.

This page was last modified on 6 янв. 2024 г. by MDN contributors.

Your blueprint for a better internet.

MDN

Support

  • Product help
  • Report an issue

Our communities

Developers

  • Web Technologies
  • Learn Web Development
  • MDN Plus
  • Hacks Blog
  • Website Privacy Notice
  • Cookies
  • Legal
  • Community Participation Guidelines

Visit Mozilla Corporation’s not-for-profit parent, the Mozilla Foundation.
Portions of this content are ©1998– 2024 by individual mozilla.org contributors. Content available under a Creative Commons license.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *