Спецификаторы, квалификаторы и шаблоны

Введение

В Java статическое ключевое слово в основном используется для управления памятью. Может использоваться с переменными, методами, блоками и вложенными классами. Это ключевое слово, которое используется для совместного использования одной и той же переменной или метода данного класса. В основном, static используется для постоянной переменной или метода, который одинаков для каждого экземпляра класса. Основной метод класса обычно помечается как статический.

Чтобы создать статический член (блок, переменную, метод, вложенный класс), вам необходимо предшествовать его объявлению с ключевым словом static. Когда член класса объявлен как статический, к нему можно получить доступ до того, как будут созданы объекты его класса, и без какой-либо ссылки на объект.

В языке программирования Java ключевое слово static является модификатором отсутствия доступа и может использоваться для следующих целей:

  • Статический блок.
  • переменная.
  • метод.
  • классы.

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

Ключевое слово static может использоваться в различных контекстах, однако сегодня речь пойдет о его использовании именно в классах C#. В русской литературе по программированию на языке C# можно встретить такие определения, как «статичный», «статический» и так далее. В любом случае, это означает, что какой-либо член класса (свойство, метод) определен с атрибутом .

В документации Microsoft по языку C# дословно сказано следующее:

Модификатор static используется для объявления статического члена, принадлежащего собственно типу, а не конкретному объекту.Microsoft

Посмотрим, что это означает на практике.

Инициализация статических полей

Чтобы инициализировать статическое поле в C# можно использовать такие варианты:

Например,как мы это сделали в нашем классе:

static int ordNumber = 0;

Использование статического конструктора

Наряду с обычными конструкторами в языке C# могут использоваться статические конструкторы, т.е конструкторы, имеющие модификатор . Такие конструкторы обладают следующими особенностями:

  1. статический конструктор нельзя перегрузить
  2. статический конструктор не принимает никаких параметров
  3. статический конструктор выполняется один раз вне зависимости от того сколько объектов данного класса создано
  4. исходя из п.3, статический конструктор не должен иметь никаких модификаторов доступа — он вызывается автоматически при создании первого объекта или при первом обращении к статическому члену класса.

Например, в нашем классе можно определить вот такой статический конструктор:

class Building
{
    static int ordNumber;
    //статический конструктор
    static Building()
    {
        ordNumber = 0;
    }
}

Инициализация статических переменных-членов внутри тела класса

Есть несколько обходных путей определения статических членов внутри тела класса. Во-первых, если статический член является константным интегральным типом (к которому относятся и char, и bool) или константным перечислением, то статический член может быть инициализирован внутри тела класса:

class Anything
{
public:
static const int s_value = 5; // статическую константную переменную типа int можно объявить и инициализировать напрямую
};

1
2
3
4
5

classAnything

{

public

staticconstints_value=5;// статическую константную переменную типа int можно объявить и инициализировать напрямую

};

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

Во-вторых, начиная с C++11 статические члены constexpr любого типа данных, поддерживающие инициализацию constexpr, могут быть инициализированы внутри тела класса:

#include <array>

class Anything
{
public:
static constexpr double s_value = 3.4; // хорошо
static constexpr std::array<int, 3> s_array = { 3, 4, 5 }; // это работает даже с классами, которые поддерживают инициализацию constexpr
};

1
2
3
4
5
6
7
8

#include <array>

classAnything

{

public

staticconstexprdoubles_value=3.4;// хорошо

staticconstexprstd::array<int,3>s_array={3,4,5};// это работает даже с классами, которые поддерживают инициализацию constexpr

};

1 Статические методы

Кроме статических переменных, в классах могут быть и статические методы.

Обычные методы привязаны к объектам (экземплярам) класса и могут обращаться к обычным-переменным класса (а также к статическим переменным и методам). Статические же методы привязаны к статическому объекту класса и могут обращаться только к статическим переменным и/или другим статическим методам класса.

Чтобы вызвать обычный метод у класса, сначала нужно создать объект этого класса, а только потом вызвать метод у объекта. Вызвать обычный метод не у объекта, а у класса нельзя.

Пример:

Вызвать нестатический метод у класса нельзя!

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

Чтобы объявить метод статическим, нужно перед заголовком метода написать ключевое слово static. Общий вид этой конструкции такой:

Примеры:

Код Примечание
Метод вызывается Java-машиной командой вида: ;
Статический метод вызывается в статическом методе .

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

Примеры:

Код Статический метод

Наследование статических методов

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

class Admin extends Users {
         constructor(name, old, login, psw) {
                   super(name, old);
                   this.login = login;
                   this.psw = psw;
         }
}

Мы расширяем
базовый класс Users и добавляем еще
два свойства: login и psw. Будет ли
статический метод compareOld доступен в дочернем классе Admin? Да, будет и,
далее, мы можем создать такого пользователя:

let u2 = new Admin("Федор", 19, "aaa", "0123");

и сравнить их,
вызывая статический метод через класс Admin:

console.log( Admin.compareOld(u1, u2) );

То есть, метод compareOld можно вызывать
и через класс Users и через класс Admin. Разницы
никакой не будет. Это происходит по той причине, что свойство __proto__ класса Admin ссылается на
класс Users:

Если же добавить
еще один статический метод, но уже в класс Admin:

         static createAdmin(name, old) {
                   return new this(name, old, "admin", "root");
         }

то картина будет
такой:

В методе createAdmin мы создаем
нового пользователя Admin с использованием только двух
параметров: name, old. Остальные два
задаются по умолчанию как: «admin», «root». Причем,
ключевое слово this здесь будет ссылаться на класс, указанный перед
точкой, при вызове данного метода. Например:

let u3 = Admin.createAdmin("Сергей", 33);

Здесь this ссылается на Admin, поэтому будет
создан новый объект класса Admin. А вот если мы в методе compareOld
добавим вывод:

console.log(this == Admin);

и вызовем его
двумя способами:

Users.compareOld(u1, u2);  // false
Admin.compareOld(u1, u2);  // true

то в первом
случае this будет ссылаться
на Users, а во втором –
на Admin.

База для getter-ов

Итак, у нас уже есть возможность выбрать базу, которая содержит указатель и определяет поведение «умного указателя». Теперь нужно снабдить эту базу методами-getter-ами. Для чего нам потребуется один простой класс:

Это шаблонный класс, который зависит от двух параметров, но их смысл уже совсем другой. В качестве параметра Base будет выступать результат показанной выше метафункции . Т.е. в качестве параметра Base задается базовый класс, от которого нужно отнаследоваться.

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

В качестве параметра Return_Type будет выступать тип сообщения, указатель/ссылку на который будет возвращаться getter-ами. Фокус в том, что для иммутабельного сообщения типа параметр Return_Type будет иметь значение . Тогда как для мутабельного сообщения типа параметр Return_Type будет иметь значение . Таким образом метод для иммутабельных сообщений будет возвращать , а для мутабельных — просто .

Посредством свободной функции решается проблема работы с сообщениями, которые не отнаследованны от :

Т.е. если сообщение не наследуется от и хранится как , то вызывается вторая перегрузка. А если наследуется, то первая перегрузка.

Выбор конкретной базы для getter-ов

Итак, шаблон требует два параметра. Первый вычисляется метафункцией . Но для того, чтобы сформировать конкретный базовый тип из , нам нужно определиться со значением второго параметра. Для этого предназначена еще одна метафункция:

Обратить внимание можно разве что на вычисление параметра Return_Type. Один из тех немногих случаев, когда east const оказывается полезен ;). Ну и, для повышения читабельности последующего кода, более компактный вариант для работы с ней:

Ну и, для повышения читабельности последующего кода, более компактный вариант для работы с ней:

shared_ptr vs unique_ptr

Третий фактор — это логика поведения как умного указателя. Когда-то он должен вести себя как , т.е. можно иметь несколько message_holder-ов, ссылающихся на один и тот же экземпляр сообщения. А когда-то он должен вести себя как , т.е. только один экземпляр message_holder-а может ссылаться на экземпляр сообщения.

По умолчанию, поведение должно зависеть от изменяемости/неизменяемости сообщения. Т.е. с неизменяемыми сообщениями должен вести себя как , а с изменяемыми, как :

Но жизнь штука сложная, поэтому нужно иметь еще и возможность вручную задать поведение . Чтобы можно было сделать message_holder-а для иммутабельного сообщения, который ведет себя как unique_ptr. И чтобы можно было сделать message_holder-а для изменяемого сообщения, который ведет себя как shared_ptr:

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

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

Что такое статические методы в Java?

Статические методы — это методы в Java, которые можно вызывать без создания объекта класса. Они задокументированы именем {class the category}. Статическое ключевое слово может использоваться с классом, переменной, методом и блоком. Статические члены принадлежат классу, а не конкретному экземпляру, это означает, что если вы сделаете член статическим, вы сможете получить к нему доступ без объекта. Давайте рассмотрим пример, чтобы понять это:

Здесь у нас есть статический метод myMethod(), мы можем вызвать этот метод без какого-либо объекта, потому что когда мы делаем член статическим, он становится уровнем класса. Если мы удалим ключевое слово static и сделаем его нестатичным, нам нужно будет создать объект класса для его вызова.

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

class SimpleStaticExample
{
    // This is a static method
    static void myMethod()
    {
        System.out.println("myMethod");
    }
 
    public static void main(String[] args)
    {
          /* You can see that we are calling this
           * method without creating any object. 
           */
           myMethod();
    }
}

Синтаксис

public static void geek(String name)
{
// code to be executed....

Он хранится в Permanent Generation, поскольку связывается с {class the category}, где они находятся, а не с объектами этого класса. Тем не менее, их локальные переменные, а также передаваемый им аргумент(ы) находятся в стеке.

Важные моменты:

  • Статический метод(ы), связанный с классом, в котором они находятся, то есть они будут ссылаться на него, даже если он не создает экземпляр класса, т.е. ClassName.methodName (args).
  • Они предназначены для совместного использования всеми объектами, созданными из одного класса.
  • Статические методы не могут быть переопределены.

Пример использования статических методов в Java:

import java.io.*;
class Flair{
   public static String FlairName = "";
   public static void geek(String name)
{
         FlairName = name;
   }
}
class GFG {
   public static void main (String[] args) {
         Flair.flair("vaibhav");
         System.out.println(Flair.flairName);
         Flair obj = new Flair ();
         obj.flair("shadow");
         System.out.println(obj.flairName);
   }
}

Вывод:

Что если статическая переменная ссылается на объект?

В первой строке значение, которое будет храниться в разделе PermGen. Во второй строке ссылка obj будет храниться в секции PermGen, а объект, на который она ссылается, будет храниться в секции heap.

Статические члены не связаны с объектами класса

Хотя вы можете получить доступ к статическим членам через разные объекты класса (как в примере, приведенном выше), но, оказывается, статические члены существуют, даже если объекты класса не созданы! Подобно глобальным переменным, они создаются при запуске программы и уничтожаются, когда программа завершает свое выполнение.

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

#include <iostream>

class Anything
{
public:
static int s_value; // объявляем статическую переменную-член
};

int Anything::s_value = 3; // определяем статическую переменную-член

int main()
{
// Примечание: Мы не создаем здесь никаких объектов класса Anything

Anything::s_value = 4;
std::cout << Anything::s_value << ‘\n’;
return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

#include <iostream>
 

classAnything

{

public

staticints_value;// объявляем статическую переменную-член

};

intAnything::s_value=3;// определяем статическую переменную-член

intmain()

{

// Примечание: Мы не создаем здесь никаких объектов класса Anything

Anything::s_value=4;

std::cout<<Anything::s_value<<‘\n’;

return;

}

В вышеприведенном фрагменте, доступ к осуществляется через имя класса, а не через объект этого класса

Обратите внимание, мы даже не создавали объект класса Anything, но мы все равно имеем доступ к и можем использовать эту переменную-член

Классы памяти переменных

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

#include <conio.h> #include <stdio.h> void main() { int x = 10; { int x = 20; { int x = 30; printf(«%d\n», x); } printf(«%d\n», x); } printf(«%d\n», x); getch(); }

идентична

#include <conio.h> #include <stdio.h> void main() { int auto x = 10; { int auto x = 20; { int auto x = 30; printf(«%d\n», x); } printf(«%d\n», x); } printf(«%d\n», x); getch(); }

Очевидно, что глобальные переменные не могут быть объявлены как auto, потому что располагаются в data-сегменте.

Следующий класс памяти – register. Когда мы определяем регистровую переменную, то мы просим компилятор, чтобы переменная располагалась в регистре, а не в оперативной памяти. Компилятор может сделать переменную регистровой, если позволяют условия (регистры не заняты, и по мнению компилятора это не приведёт к увеличению издержек). Регистровые переменные определяются с помощью служебного слово register перед типом

register int x = 20; register int y = 30;

Так как регистровая переменная не имеет адреса, то к ней не применима операция взятия адреса, это вызовет ошибку во время компиляции. Аргументы функции также могут быть заданы как register. Внутри функции они будут вести себя также, как и регистровые переменные.

Следующий класс памяти – статический. Переменные, объявленные как static, хранятся в data или в bss сегменте. Отличительной чертой является то, что время их жизни совпадает с временем жизни приложения, как и у глобальных переменных. Но в отличие от глобальных переменных, область видимости ограничена только блоком, в котором они определены.

#include <conio.h> #include <stdio.h> unsigned long long factorial(unsigned char n) { static unsigned char prevArg = 0; static long long prevAns = 1; if (n == prevArg) { printf(«return previous answer\n»); return prevAns; } else { unsigned i = 0; printf(«count new answer\n»); prevAns = 1; for (i = 1; i <= n; i++) { prevAns *= i; } prevArg = n; return prevAns; } } void main() { printf(«!%d == %llu\n», 10, factorial(10)); printf(«!%d == %llu\n», 10, factorial(10)); printf(«!%d == %llu\n», 11, factorial(11)); printf(«!%d == %llu\n», 11, factorial(11)); getch(); }

В этом примере переменные prevArg и prevAns инициализируются единожды, и не уничтожаются после выхода из функции. Переменная prevArg используется для хранения предыдущего аргумента функции, а prevAns для хранения предыдущего результата. Если аргумента функции совпадает с предыдущим, то возвращается ранее вычисленное значение, иначе оно вычисляется по-новому.

Другой показательный пример – функция-генератор, которая при каждом вызове возвращает новое значение.

#include <conio.h> #include <stdio.h> int next() { static int counter = 0; counter++; return counter; } void main() { printf(«%d\n», next()); printf(«%d\n», next()); printf(«%d\n», next()); printf(«%d\n», next()); printf(«%d\n», next()); _getch(); }

Если бы служебное слово static отсутствовало, то каждый раз при вызове функции локальная переменная counter снова создавалась, инициализировалась и уничтожалась после выхода из функции.

Статическая переменная может иметь только константную инициализацию. Например, она не может быть инициализирована вызовом функции.

… static double x = foo(3); //Ошибка …

Переменная, объявленная как static, должна иметь только один экземпляр в данной области видимости и вне этой области видимости не видна. Глобальная переменная, объявленная как static, видна только в своём файле.

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

Статические классы

Кроме того, что вы можете объявлять статические члены класса (поля, методы, свойства), в C# вы можете объявлять статическими целые классы. Объявить статический класс можно так:

static class SomeClass
 {

 }

при этом следует помнить, что если к классу применяется ключевое слово , все члены этого класса должны быть .  Где вы можете встретить статический класс C#? Одним из самых показательных примеров может быть тот самый класс , методами которого мы пользуемся с самого начала знакомства с языком C#.  Чтобы мы не считывали или не записывали в консоль — мы всегда используем статические методы статического класса (не создаем объект типа , а используем методы класса).

Статические методы

Если статические переменные-члены являются открытыми, то мы можем получить к ним доступ напрямую через имя класса и оператор разрешения области видимости. Но что, если статические переменные-члены являются закрытыми? Рассмотрим следующий код:

class Anything
{
private:
static int s_value;

};

int Anything::s_value = 3; // определение статического члена, несмотря на то, что он является private

int main()
{
// Как получить доступ к Anything::s_value здесь, если s_value является private?
}

1
2
3
4
5
6
7
8
9
10
11
12
13

classAnything

{

private

staticints_value;

};

intAnything::s_value=3;// определение статического члена, несмотря на то, что он является private

intmain()

{

// Как получить доступ к Anything::s_value здесь, если s_value является private?

}

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

Подобно статическим переменным-членам, статические методы не привязаны к какому-либо одному объекту класса. Вот вышеприведенный пример, но уже со статическим методом:

class Anything
{
private:
static int s_value;
public:
static int getValue() { return s_value; } // статический метод
};

int Anything::s_value = 3; // определение статической переменной-члена класса

int main()
{
std::cout << Anything::getValue() << ‘\n’;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14

classAnything

{

private

staticints_value;

public

staticintgetValue(){returns_value;}// статический метод

};

intAnything::s_value=3;// определение статической переменной-члена класса

intmain()

{

std::cout<<Anything::getValue()<<‘\n’;

}

Поскольку статические методы не привязаны к определенному объекту, то их можно вызывать напрямую через имя класса и оператор разрешения области видимости, а также через объекты класса (но это не рекомендуется).

От чего отнаследован тип сообщения?

Типы сообщений, которыми параметризуется , делятся на две группы. Первая группа — это сообщения, которые наследуются от специального базового типа . Например:

В этом случае message_holder_t внутри себя должен содержать только указатель на объект этого типа. Этот же указатель должен возвращаться в методах-getter-ах. Т.е., для случая наследника от должно быть что-то вроде:

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

Экземпляры таких типов в SObjectizer-е отсылаются не сами по себе, а заключенными в специальную обертку , которая уже наследуется от . Поэтому для таких типов должен содержать внутри себя указатель на , а методы-getter-ы должны возвращать указатель на M:

ПримерExample

В следующем примере директива используется для того, чтобы доступ к статическим членам классов Console, Math и String можно было получать, не указывая имя типа.The following example uses the directive to make the static members of the Console, Math, and String classes available without having to specify their type name.

В этом примере директива может также применяться к типу Double.In the example, the directive could also have been applied to the Double type. В этом случае вызвать метод , не указав имя типа, было бы возможно.This would have made it possible to call the method without specifying a type name. При этом код становится менее понятным, поскольку появляется необходимость проверять директивы и определять, какой метод числового типа вызывается.However, this creates less readable code, since it becomes necessary to check the directives to determine which numeric type’s method is called.

Nginx¶

См.также

http://nginx.org/ru/docs/beginners_guide.html#static

Для примера возьмем следующую структуру файлов:

/usr/share/nginx/html/
|-- index.html
|-- index2.html
`-- static_example
    `-- static
        |-- html-css-js.png
        |-- jquery.min.js
        |-- script.js
        `-- style.css

2 directories, 6 files

страница, которая ссылается на другие статические файлы.

index2.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <title>Untitled Document</title>
  <link href="/static/style.css" media="all" rel="stylesheet" type="text/css" />
  <script src="/static/jquery.min.js"></script>
  <script src="/static/script.js"></script>
</head>
<body>
<table width="100%" border="1" cellspacing="10" cellpadding="10">
  <tr>
    <td width="60%">
    <h1>HTML</h1>
    <a href="">HTML</a>
    <a href="">JS</a>
    <a href="" style="color: green">CSS</a>
    <hr/>
    <p class="replace-text">HTML (от англ. HyperText Markup Language — «язык
    гипертекстовой разметки») — стандартный язык разметки документов во
    Всемирной паутине. Большинство веб-страниц содержат описание разметки на
    языке HTML (или XHTML). Язык HTML интерпретируется браузерами и
    отображается в виде документа в удобной для человека форме.</p> <p
    style="color: black; background-color: red; color: #fff"> Язык HTML
    является приложением («частным случаем») SGML (стандартного обобщённого
    языка разметки) и соответствует международному стандарту ISO 8879.</p>
    <p class="hide"> Язык XHTML является более строгим вариантом HTML, он
    следует всем ограничениям XML и, фактически, XHTML можно воспринимать
    как приложение языка XML к области разметки гипертекста.</p> <p> Во
    всемирной паутине HTML-страницы, как правило, передаются браузерам от
    сервера по протоколам HTTP или HTTPS, в виде простого текста или с
    использованием сжатия.</p>
    <hr/>
    </td>
    <td width="40%"><img src="/static/html-css-js.png" class="jquery-image"
      width="500" height="293" alt="HTML JS CSS"></td> </tr>
</table>
</body>
</html>

Сервер Nginx настроен таким образом, что по адресу отдается
страница , а по файлы из директории
.

/etc/nginx/sites-enabled/default.nginx

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# default.nginx

server {
    listen 80 default_server;

    root /usr/share/nginx/html;
    index index.html index.htm;

    include includes/fcgi.nginx;
    include includes/static.nginx;
}

/etc/nginx/includes/static.nginx

1
2
3
4
5
6
7
location  /example {
    try_files $uri $uri/ /index2.html;
}

location /static {
    alias /usr/share/nginx/html/static_example/static;
}

Пример index2.html без статики

Если скопировать файлы статики в директорию
, то сервер начнет их отдавать:

1 Статические переменные

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

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

Статическая (static) же переменная привязана к статическому объекту класса и всегда существует в единственном экземпляре.

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

Если статической переменной не присвоить стартовое значение, она инициализируется значением по умолчанию:

Тип Значение по умолчанию
(то же самое, что и )
и любые классы

Примеры:

Код Примечание

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

Пример:

Переменная Класс Обращение к переменной вне класса
Переменная , вне класса не видна
Переменная , вне класса не видна
Переменная , вне класса не видна

ПримерExample

В этом примере класс имеет статический конструктор.In this example, class has a static constructor. При создании первого экземпляра класса () для инициализации класса вызывается статический конструктор.When the first instance of is created (), the static constructor is invoked to initialize the class. В выходных данных этого примера можно увидеть, что статический конструктор выполняется только один раз, несмотря на то, что создается два экземпляра класса . Кроме того, этот конструктор вызывается до выполнения конструктора экземпляра.The sample output verifies that the static constructor runs only one time, even though two instances of are created, and that it runs before the instance constructor runs.

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

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

Adblock
detector