Чем отличается статический метод от обычного. Статические свойства и методы. Что должен знать каждый программист о модификаторе Static в Java

Вы можете объявить некоторые методы класса статическими методами. Для этого вы должны воспользоваться ключевым словом static. Статические методы не принимают параметр this. На использование статических методов накладывается ряд ограничений.

    Статические методы могут непосредственно обращаться только к статическим членам класса.

    Статический метод не может быть объявлен как виртуальный метод.

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

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

Ниже представлен класс Circle, в котором определена статический метод GetPi. Он используется для получения значения статического элемента класса fPi.

static void GetPi()

static float fPi;

float Circle::fPi = 3.1415;

Вы можете вызвать метод GetPi следующим образом:

fNumber = Circle::GetPi();

Обратите внимание, что объект класса Circle не создается.

Общие члены объектов класса

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

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

{ public: int xLeftTop, xRightBottom; int yLeftTop, yRightBottom; static char title; void SetTitle(char*); };char Cwindow::title = “заголовок окна”;

Каждый объект класса Cwindow будет иметь уникальные координаты, определяемые элементами данных xLeftTop, xRightBottom, yLeftTop, yRightBottom и одинаковый заголовок, хранимый элементом данных title.

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

void SetTitle(char* sSource)

{ strcpy(title, sSource); }

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

printf(Cwindow::title);

Дружественные функции и дружественные классы

Доступ к элементам класса из программы и других классов ограничен. Вы можете непосредственно обращаться только к элементам класса, определенным или описанным после ключевого слова public. Однако, в некоторых случаях, требуется определить функцию вне класса или другой класс, методы которого могут обращаться непосредственно ко всем элементам класса, включая элементы объявленные как private и protect.

Дружественные функции

В Си++ вы можете определить для класса так называемую дружественную функцию, воспользовавшись ключевым словом friend. В классе содержится только объявление дружественной функции. Ее определение расположено вне класса. Вы можете объявить дружественную функцию в любой секции класса -public, private или protect.

Дружественная функция не является элементом класса, но может обращаться ко всем его элементам, включая private и protect. Одна и та же функция может быть дружественной для двух или более классов.

В следующем примере определена функция Clear, дружественная для классаpoint. Дружественная функция Clearиспользуется для изменения значения элементов данныхm_xиm_y, объявленных как private:

// Класс point

// Функция Clear объявляется дружественной классу point

friend void point::Clear(point*);

// Интерфейс класса...

//==========================================================

// Функция Clear

void Clear(point* ptrPoint)

// Обращаемся к элементам класса, объявленным как private

ptrPoint->m_x = 0;

ptrPoint->m_y = 0;

//==========================================================

// Главная функция

point pointTestPoint;

// Вызываем дружественную функцию

Clear(&pointTestPoint);

С помощью ключевого слова friend вы можете объявить некоторые методы одного класса дружественными для другого класса. Такие методы могут обращаться ко всем элементам класса, даже объявленным как private и protect, несмотря на то, что сами они входят в другой класс.

В следующем примере мы определяем два класса - line и point. В классе point определяем метод Set и объявляем его в классе line как дружественный. Дружественный метод Set может обращаться ко всем элементам класса line:

// Предварительное объявление класса line

//==========================================================

// Класс point

// Метод Set класса point

void Set(line*);

//==========================================================

// Класс line

// Метод Set класса point объявляется дружественной

// классу point

friend void point::Set(line*);

int begin_x, begin_y;

int end_x, end_y;

//==========================================================

// Функция Clear

void point::Set(line* ptrLine)

// Обращаемся к элементам класса line, объявленным как

ptrLine->begin_x = 0;

ptrLine->begin_y = 0;

//==========================================================

// Главная функция

point pointTestPoint;

line lineTestPoint;

// Вызываем дружественный метод

pointTestPoint.Set(&lineTestPoint);

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

Чем статика может быть опасна?
Представь, что ты пишешь утилиту для загрузки изображений. И вот наступил момент, когда нужно указывать параметры обрезки для превьюшек. Со статикой это может выглядеть так:

Public function upload() { $width = Config::read("width"); $height = Config::read("height"); // .. Do upload.. }
(На заметку CakePHP кишит такими подходами)
Проблемы такого подхода?

1. Нужно знать и быть точно уверенным, что статичный класс Config, был где-то там далеко инициализирован. А вдруг он не был инициализированным?

2. А что если ты решишь сменить источник конфига? Например читать, это все не из класса `Config` а откуда-нибудь из REST? Придется все переписывать, затем опять тестировать. Эта проблема известна как сильная связка .

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

4. Скрытые зависимости.

Например инициализуруя класс, в случае статики:

$uploader = new Uploader(); $uploader->upload(...);

Ты и твои пользователи не видят какие у класса зависимости и от работы чего вообще он зависит. Проблема заметна особо, когда ты пишешь библиотеку или компонет.

Но тогда почему такие популярные фреймворки как Yii или Laravel полностью покрыты статикой?

Для того чтобы понять какие последствия несет статика, попробуй воспользоватся хоть одним компонентом Yii фреймворка отдельно. Например, если тебе нужна только CAPTCHA ты не сможешь её вытащить оттуда не переписав почти весь компонент, потому что везде внутри присутвует глобальное состояние, в виде `Yii::$app->`. То есть чтобы воспользоватся только капчей, придется подключать весь фреймворк и все его внутренние механизмы, когда это абсолютно не нужно.

Что касается Laravel, то статики там меньше, поскольку некоторые компоненты, вроде Eloquent могут использоватся по отдельности. Статика в ларе, она существует как обёртка, но не как реализация, во многих местах в отличии от Yii.

Вообще посмотри, как задачи решаются в Zend / Symfony
Там почти везде все зависимости передаются в качестве аргументов, что есть хорошо, слабо связано, и тестируемо.

Что такое static

В некоторых случаях желательно определить член класса, который будет использоваться независимо от любого объекта этого класса. Обычно обращение к члену класса должно выполняться только в сочетании с объектом его класса. Однако можно создать член класса, который может использоваться самостоятельно, без ссылки на конкретный экземпляр. Чтобы создать такой член, в начало его объявления нужно поместить ключевое слово static. Когда член класса объявлен как static (статический), он доступен до создания каких-либо объектов его класса и без ссылки на какой-либо объект. Статическими могут быть объявлены как методы, так и переменные. Наиболее распространенный пример статического члена - метод main (). Этот метод объявляют как static, поскольку он должен быть объявлен до создания любых объектов.

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

На методы, объявленные как static, накладывается ряд ограничений.

  • Они могут вызывать только другие статические методы.
  • Они должны осуществлять доступ только к статическим переменным.
  • Они ни коим образом не могут ссылаться на члены типа this или super. (Ключевое слово super связано с наследованием и описывается в следующей главе.)

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

// Демонстрация статических переменных, методов и блоков.
class UseStatic {
static int a = 3;
static int b;
static void meth(int x) {
System.out.println ("x = " + x) ;
System.out.println ("a = " + a);
System.out.println("b = " + b) ;
}
static {
System.out.println("Статический блок инициализирован.");
b = a * 4;
}

meth(42);
}
}

Сразу после загрузки класса UseStatic программа выполняет все операторы static. Вначале значение а устанавливается равным 3, затем программа выполняет блок static, который выводит сообщение, а затем инициализирует переменную b значением а*4, или 12. Затем программа вызывает метод main (), который обращается к методу meth (), передавая параметру х значение 42. Три оператора println () ссылаются на две статических переменные а и b на локальную переменную х.

Вывод этой программы имеет такой вид:

Статический блок инициализирован, х = 42 а = 3 b = 12

За пределами класса, в котором они определены, статические методы и переменные могут использоваться независимо от какого-либо объекта. Для этого достаточно указать имя их класса, за которым должна следовать операция точки. Например, если метод типа static нужно вызвать извне его класса, это можно выполнить, используя следующую общую форму:

имя_класса.метод()

Здесь имя_класса - имя класса, в котором объявлен метод тип static. Как видите, этот формат аналогичен применяемому для вызова нестатических методов через переменные объектных ссылок. Статическая переменная доступна аналогичным образом - посредством операции точки, следующей за именем класса. Так в Java реализованы управляемые версии глобальных методов и переменных.

Приведем пример. Внутри метода main () обращение к статическому методу callme () и статической переменной b осуществляется посредством имени их класса StaticDemo.

class StaticDemo {
static int a = 42;
static int b = 99;
static void callme () {
System.out.println("a = " + a);
}
}
class StaticByName {
public static void main(String args) {
StaticDemo.callme () ;
System.out.println("b = " + StaticDemo.b);
}
}

Вывод этой программы выглядит следующим образом.

Объявление методов.

Тема 9. Методы

Какбыло отмечено ранее С# поддерживает большой набор функций-членов, которые имеют различные имена и предназначены для разных целей. Тем неменее, все они содержат блоки операторов, которые исполняются при вызове точно также, как это происходит для стандартного метода.

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

Метод экземпляра исполняется для конкретного объекта, а статический метод - для класса, поэтому при вызове последнего применяется имя класса, а не объекта.Все объекты одного класса исполь­зуют одну копию метода экземпляра.

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

Уровень доступа к методу определяется необязательным спецификатором в его за­головке.

На данный момент применялись два из возможных спецификаторов: public иprivate, public указывает, что к методу имеет доступ любая часть программы, a private ограничивает его применение за пределами класса.

Приведем синтаксический блок применения метода.

Метод::=

<3аголовок_метода>

<Тело_метода>

<3аголовок_метода>::=[<Спецификаторы_метода>] <Тип_возвращаемого_значения> <Идентификатор метода> ([<Список_формальных_параметров>])

< Тело_метода>: :=

<Операторы>

<Спецификаторы_метода>

::=<Спецификатор_доступности>

<Спецификатор_доступности>

::= public

::= private

<Тип_возвращаемого_значения>

::= <Тип>

Вызов метода состоит из его имени, за которым следует пара круглых скобок со спис­ком аргументов, последний должен соответствовать списку формальных параметров, определенных в заголовке метода.

Привести пример.

Известно, что исполнение программы в среде.NET начинается с вызова метода Main(). Среда исполнения не создает никаких объектов, поэтому Main() должен вызы­ваться независимо от них. Объявление метода Main() статическим указывает, что он принадлежит классу. Тем самым, среда исполнения вызывает Main () непосредственно через имя класса.

Статический метод можно вызвать тремя способами:

1. Из объекта класса, которому он принадлежит. В этом случае префикс (имя класса или объекта) не нужен.

public static void Average(…)



2. Извне данного класса.

При этом существуют две возможности:

1. Если существует объект класса, где метод определен, вызов последнего состоит из имени объекта, операции уточнения и имени метода:

myClass A = new myClass();

2. Независимо от существования объектов класса, а котором метод определен, он вызывается для класса посредством операции уточнения:

MyClass.Average(. ..)...

В вызоае метода предпочтительнее использовать имя класса (myClass.Average(...)), а не объекта A.Average(...) поскольку первый вариант четко указывает, что вызывается статический метод.

Последнее обновление: 25.12.2018

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

Class Account { public static decimal bonus = 100; public decimal totalSum; public Account(decimal sum) { totalSum = sum + bonus; } } class Program { static void Main(string args) { Console.WriteLine(Account.bonus); // 100 Account.bonus += 200; Account account1 = new Account(150); Console.WriteLine(account1.totalSum); // 450 Account account2 = new Account(1000); Console.WriteLine(account2.totalSum); // 1300 Console.ReadKey(); } }

В данном случае класс Account имеет два поля: bonus и totalSum. Поле bonus является статическим, поэтому оно хранит состояние класса в целом, а не отдельного объекта. И поэтому мы можем обращаться к этому полю по имени класса:

Console.WriteLine(Account.bonus); Account.bonus += 200;

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

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

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

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

Class Account { public Account(decimal sum, decimal rate) { if (sum < MinSum) throw new Exception("Недопустимая сумма!"); Sum = sum; Rate = rate; } private static decimal minSum = 100; // минимальная допустимая сумма для всех счетов public static decimal MinSum { get { return minSum; } set { if(value>0) minSum = value; } } public decimal Sum { get; private set; } // сумма на счете public decimal Rate { get; private set; } // процентная ставка // подсчет суммы на счете через определенный период по определенной ставке public static decimal GetSum(decimal sum, decimal rate, int period) { decimal result = sum; for (int i = 1; i <= period; i++) result = result + result * rate / 100; return result; } }

Переменная minSum, свойство MinSum, а также метод GetSum здесь определены с ключевым словом static , то есть они являются статическими.

Переменная minSum и свойство MinSum представляют минимальную сумму, которая допустима для создания счета. Этот оказатель не относится к какому-то конкретному счету, а относится ко всем счетам в целом. Если мы изменим этот показатель для одного счета, то он также должен измениться и для другого счета. То есть в отличии от свойств Sum и Rate, которые хранят состояние объекта, переменная minSum хранит состояние для всех объектов данного класса.

То же самое с методом GetSum - он вычисляет сумму на счете через определенный период по определенной процентной ставке для определенной начальной суммы. Вызов и результат этого метода не зависит от конкретного объекта или его состояния.

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

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

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

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

Class User { private static int counter = 0; public User() { counter++; } public static void DisplayCounter() { Console.WriteLine($"Создано {counter} объектов User"); } } class Program { static void Main(string args) { User user1 = new User(); User user2 = new User(); User user3 = new User(); User user4 = new User(); User user5 = new User(); User.DisplayCounter(); // 5 Console.ReadKey(); } }

Статический конструктор

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

    Статические конструкторы не должны иметь модификатор доступа и не принимают параметров

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

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

Статические конструкторы обычно используются для инициализации статических данных, либо же выполняют действия, которые требуется выполнить только один раз

Определим статический конструктор:

Class User { static User() { Console.WriteLine("Создан первый пользователь"); } } class Program { static void Main(string args) { User user1 = new User(); // здесь сработает статический конструктор User user2 = new User(); Console.Read(); } }

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

Статические классы объявляются с модификатором static и могут содержать только статические поля, свойства и методы. Например, если бы класс Account имел бы только статические переменные, свойства и методы, то его можно было бы объявить как статический:

Static class Account { private static decimal minSum = 100; // минимальная допустимая сумма для всех счетов public static decimal MinSum { get { return minSum; } set { if(value>0) minSum = value; } } // подсчет суммы на счете через определенный период по определенной ставке public static decimal GetSum(decimal sum, decimal rate, int period) { decimal result = sum; for (int i = 1; i <= period; i++) result = result + result * rate / 100; return result; } }

В C# показательным примером статического класса является класс Math, который применяется для различных математических операций.