Коли використовувати static

Ключове слово static використовується для створення змінних, які будуть видно тільки одну функцію. Однак, на відміну від локальних змінних, які створюються та знищуються при кожному виклику функції, змінні static зберігають своє значення між викликами.

Коли використовувати static Java?

Статичний блок використовується для ініціалізації статичних змінних. Хоча статичні змінні можуть бути ініціалізовані безпосередньо під час оголошення, бувають ситуації, коли нам потрібно виконати багаторядкову обробку. У таких випадках стануть у нагоді статичні блоки.

Навіщо використовується static?

Модифікатор static використовується для оголошення статичного члена, що належить власне типу, а чи не конкретному об'єкту. Модифікатор static можна використовувати для оголошення класів static.

Чим відрізняється static змінна від звичайної?

Статичні змінні класу завжди знаходяться всередині статичного об'єкта класу та існують лише в одному екземплярі. Звертатися до звичайним змінним класу (полям класу) можна лише маючи посилання об'єкт класу.

TopJava
Посібник з модифікатора static в Java

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

Ключове слово мови Java static використовується для оголошення статичних членів класу – методів та змінних. Також бувають статичні блоки.

Різниця між статичними даними та звичайними членами класу полягає в тому, що звернення до звичайного члена класу має здійснюватися лише у поєднанні з об'єктом його класу. Коли ж член класу оголошений із ключовим словом static, він доступний і без посилання на якийсь об'єкт.

Презентацію з відео можна завантажити на Patreon.

Зареєструйтесь або увійдіть, щоб мати можливість залишити коментар.

Статичні властивості та методи

Ми також можемо надати метод самому класу. Такі методи називаються статичними.

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

class User < static staticMethod() < alert(this === User); >> User.staticMethod(); // true

Це практично те саме, що присвоїти метод безпосередньо як властивість функції:

class User < >User.staticMethod = function() < alert(this === User); >;

Значенням цього при виклику User.staticMethod() є сам конструктор класу User (правило «об'єкт до точки»).

Зазвичай статичні методи використовуються для реалізації функцій, що належать класу в цілому, але не будь-якому конкретному об'єкту.

Звучить не дуже зрозуміло? Нині все стане на свої місця.

Наприклад, є об'єкти статей Article , і потрібна функція їхнього порівняння.

Природне рішення – зробити при цьому статичний метод Article.compare :

class Article < constructor(title, date) < this.title = title; this.date = date; >static compare(articleA, articleB) < return articleA.date - articleB.date; >> // використання let articles = [ new Article("HTML", new Date(2019, 1, 1)), new Article("CSS", new Date(2019, 0, 1)), new Article("JavaScript ", New Date (2019, 11, 1))]; articles.sort(Article.compare); alert(articles[0].title); // CSS

Тут метод Article.compare стоїть «над» статтями, як їхнього порівняння. Це спосіб не окремої статті, а всього класу.

Іншим прикладом може бути так званий "фабричний" метод.

Скажімо, нам потрібні кілька способів створення статті:

  1. Створення через задані параметри (title, date тощо).
  2. Створення порожній статті з датою.
  3. …або якось ще.

Перший спосіб може бути реалізований через конструктор. А для другого можна використати статичний метод класу.

Такий як Article.createTodays() в наступному прикладі:

class Article < constructor(title, date) < this.title = title; this.date = date; >static createTodays() < // пам'ятаємо, що this = Article return new this("Сьогоднішній дайджест", new Date()); >> let article = Article.createTodays(); alert (article.title); // Сьогоднішній дайджест

Тепер щоразу, коли нам потрібно створити сьогоднішній дайджест, потрібно викликати Article.createTodays(). Ще раз це не метод однієї статті, а метод всього класу.

Статичні методи також використовуються в класах, що належать до баз даних, для пошуку/збереження/видалення входжень до бази даних, наприклад:

// припустимо, що Article - це спеціальний клас управління статтями // статичний метод видалення статті з id: Article.remove();

Статичні методи можуть викликатися класів, але з окремих об'єктів.

Наприклад. такий код не працюватиме:

//. article.createTodays(); /// Error: article.createTodays is not a function

Статичні властивості

Статичні властивості також можливі, вони виглядають як властивості класу, але з static на початку:

class Article < static publisher = "Ілля Кантор"; > alert (Article.publisher); // Ілля Кантор

Це те саме, що і пряме присвоєння Article :

Article.publisher = "Ілля Кантор";

Спадкування статичних властивостей та методів

Статичні властивості та методи успадковуються.

Наприклад, метод Animal.compare у коді нижче успадковується і доступний як Rabbit.compare :

class Animal < constructor(name, speed) < this.speed = speed; this.name = name; > run (speed = 0) < this.speed + = speed; alert(`$біжить зі швидкістю $.`); > static compare(animalA, animalB) < return animalA.speed - animalB.speed; >> // Успадковує від Animal class Rabbit extends Animal < hide() < alert(`$ховається!`); > > let rabbits = [new Rabbit("Білий кролик", 10), new Rabbit("Чорний кролик", 5)]; rabbits.sort(Rabbit.compare); rabbits[0].run(); // Чорний кролик біжить зі швидкістю 5.

Ми можемо викликати Rabbit.compare, при цьому буде викликаний успадкований Animal.compare.

Як це працює? Знову з використанням прототипів. Як ви вже могли припустити, extends дає Rabbit посилання [[Prototype]] на Animal .

Так що Rabbit extends Animal створює два посилання на прототип:

  1. Функція Rabbit прототипно успадковує від функції Animal.
  2. Rabbit.prototype прототипно успадковує від Animal.prototype.

В результаті успадкування працює як для звичайних, так статичних методів.

Давайте це перевіримо кодом:

class Animal <> class Rabbit extends Animal <> // для статики alert(Rabbit.__proto__ === Animal); // true // для звичайних методів alert(Rabbit.prototype.__proto__ === Animal.prototype); // true

Разом

Статичні методи використовуються для функціональності, належать класу "загалом", а не відносяться до конкретного об'єкта класу.

Наприклад, метод для порівняння двох статей Article.compare(article1, article2) або фабричний метод Article.createTodays() .

У оголошенні класу вони позначаються ключовим словом static.

Статичні властивості використовуються у тих випадках, коли ми хотіли б зберегти дані на рівні класу, а не якогось одного об'єкта.

Технічно, статичне оголошення – це те саме, що і присвоєння класу:

MyClass.property = . MyClass.method = . 

Статичні властивості та методи успадковуються.

Для class B extends A прототип класу B свідчить про A : B.[[Prototype]] = A . Таким чином, якщо поле не знайдено в B, пошук продовжується в A.

Завдання

Клас розширює об'єкт?

Як ми вже знаємо, всі об'єкти успадковують від Object.prototype і мають доступ до «загальних» методів об'єкта, наприклад hasOwnProperty.

class Rabbit < constructor(name) < this.name = name; >> let rabbit = новий Rabbit("Rab"); // метод hasOwnProperty від Object.prototype alert(rabbit.hasOwnProperty('name')); // true

Але якщо ми явно напишемо "class Rabbit extends Object" – тоді результат буде відрізнятися від звичайного "class Rabbit"?

Нижче приклад коду з таким спадкуванням (чому він не працює? Виправте його):

class Rabbit extends Object < constructor(name) < this.name = name; >> let rabbit = new Rabbit("Кроль"); alert(rabbit.hasOwnProperty('name')); // Помилка

Спочатку давайте розберемося, чому код не працює.

Причина стає очевидною, якщо ми спробуємо запустити його.Успадкований конструктор класу повинен викликати super(). В іншому випадку "this" буде не визначено.

class Rabbit extends Object < constructor(name) < super(); // Треба викликати конструктор батька, коли успадковуємо this.name = name; >> let rabbit = new Rabbit("Кроль"); alert(rabbit.hasOwnProperty('name')); // true

Навіть після виправлення є важлива різниця між "class Rabbit extends Object" та class Rabbit .

Як ми знаємо, синтаксис extends встановлює 2 прототипи:

  1. Між "прототипом" функцій-конструкторів (для методів)
  2. між самими функціями-конструкторами (для статичних методів).

У випадку з class Rabbit extends Object це означає:

class Rabbit extends Object <> alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true alert( Rabbit.__proto__ === Object ); // (2) true

Таким чином, Rabbit надає доступ до статичних методів Object через Rabbit, наприклад:

class Rabbit extends Object <> // зазвичай ми викликаємо Object.getOwnPropertyNames alert( Rabbit.getOwnPropertyNames() ); // a, b

Але якщо явно не успадковуємо від об'єкта, то Rabbit.__proto__ не встановлено значення Object .

class Rabbit <> alert( Rabbit.prototype.__proto__ === Object.prototype ); // (1) true alert( Rabbit.__proto__ === Object ); // (2) false (!) alert (Rabbit.__proto__ === Function.prototype); // як кожна функція за замовчуванням // помилка - немає такої функції у Rabbit alert( Rabbit.getOwnPropertyNames() ); // Помилка

Таким чином, у цьому випадку Rabbit не має доступу до статичних методів Object .

До речі, Function.prototype також має «загальні» методи, такі як call , bind і т. д. Вони в кінцевому підсумку доступні в обох випадках, тому що для вбудованого конструктора Object Object.

Коротше кажучи, є дві відмінності:

class Rabbitclass Rabbit extends Object
необхідно викликати super() у конструкторі
Rabbit.__proto__ === Function.prototypeRabbit.__proto__ === Object