Статические методы в java(static methods)

Зачем нужен dynamic_cast?

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

#include <iostream>
#include <string>

class Parent
{
protected:
int m_value;

public:
Parent(int value)
: m_value(value)
{
}

virtual ~Parent() {}
};

class Child: public Parent
{
protected:
std::string m_name;

public:
Child(int value, std::string name)
: Parent(value), m_name(name)
{
}

const std::string& getName() { return m_name; }
};

Parent* getObject(bool bReturnChild)
{
if (bReturnChild)
return new Child(1, «Banana»);
else
return new Parent(2);
}

int main()
{
Parent *p = getObject(true);

// Как мы выведем имя объекта класса Child здесь, имея лишь один указатель класса Parent?

delete p;

return 0;
}

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
40
41
42
43
44
45
46
47
48
49

#include <iostream>
#include <string>

classParent

{

protected

intm_value;

public

Parent(intvalue)

m_value(value)

{

}

virtual~Parent(){}

};

classChildpublicParent

{

protected

std::stringm_name;

public

Child(intvalue,std::stringname)

Parent(value),m_name(name)

{

}

conststd::string&getName(){returnm_name;}

};

Parent*getObject(boolbReturnChild)

{

if(bReturnChild)

returnnewChild(1,»Banana»);

else

returnnewParent(2);

}

intmain()

{

Parent*p=getObject(true);

// Как мы выведем имя объекта класса Child здесь, имея лишь один указатель класса Parent?

deletep;

return;

}

В этой программе метод getObject() всегда возвращает указатель класса Parent, но этот указатель может указывать либо на объект класса Parent, либо на объект класса Child. В случае, когда указатель указывает на объект класса Child, как мы будем вызывать Child::getName()?

Один из способов — добавить виртуальную функцию getName() в класс Parent (чтобы иметь возможность вызывать переопределение через объект класса Parent). Но, используя этот вариант, мы будем загромождать класс Parent тем, что должно быть заботой только класса Child.

Язык C++ позволяет нам неявно конвертировать указатель класса Child в указатель класса Parent (фактически, это и делает getObject()). Эта конвертация называется приведением к базовому типу (или «повышающим приведением типа»). Однако, что, если бы мы могли конвертировать указатель класса Parent обратно в указатель класса Child? Таким образом, мы могли бы напрямую вызывать Child::getName(), используя тот же указатель, и вообще не заморачиваться с виртуальными функциями.

База для getter-ов

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

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

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

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

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

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

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

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

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

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

Свойства

В отличие от обычных свойств, изменение значения статического свойства во время выполнения программы повлияет на все экземпляры содержащего свойство класса. Даже на те, для которых еще не созданы экземпляры. Таким образом, статические свойства можно рассматривать в качестве «констант изменяемого класса». На статические свойства можно ссылаться только с помощью оператора разрешения области видимости.

Благодаря природе статических свойств их можно использовать для реализации шаблона одиночка (Singleton). Одиночка содержит один и тот же экземпляр класса на протяжении всего выполнения программы.

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

Понижающее приведение и оператор static_cast

Оказывается, понижающее приведение также может быть выполнено и через оператор static_cast. Основное отличие заключается в том, что static_cast не выполняет проверку во время запуска программы, чтобы убедиться в том, что вы делаете то, что имеет смысл. Это позволяет оператору static_cast быть быстрее, но опаснее оператора dynamic_cast. Если вы будете конвертировать Parent* в Child*, то операция будет «успешной», даже если указатель класса Parent не будет указывать на объект класса Child. А сюрприз вы получите тогда, когда попытаетесь получить доступ к этому указателю (который после конвертации должен быть класса Child, но, фактически, указывает на объект класса Parent).

Если вы абсолютно уверены, что операция с понижающим приведением указателя будет успешна, то использование static_cast является приемлемым. Один из способов убедиться в этом — использовать виртуальную функцию:

#include <iostream>
#include <string>

// Идентификаторы классов
enum ClassID
{
PARENT,
CHILD
// Здесь можно добавить еще несколько классов
};

class Parent
{
protected:
int m_value;

public:
Parent(int value)
: m_value(value)
{
}

virtual ~Parent() {}
virtual ClassID getClassID() { return PARENT; }
};

class Child: public Parent
{
protected:
std::string m_name;

public:
Child(int value, std::string name)
: Parent(value), m_name(name)
{
}

std::string& getName() { return m_name; }
virtual ClassID getClassID() { return CHILD; }

};

Parent* getObject(bool bReturnChild)
{
if (bReturnChild)
return new Child(1, «Banana»);
else
return new Parent(2);
}

int main()
{
Parent *p = getObject(true);

if (p->getClassID() == CHILD)
{
// Мы уже доказали, что p указывает на объект класса Child, поэтому никаких проблем здесь не должно быть
Child *ch = static_cast<Child*>(p);
std::cout << «The name of the Child is: » << ch->getName() << ‘\n’;
}

delete p;

return 0;
}

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

#include <iostream>
#include <string>

// Идентификаторы классов

enumClassID

{

PARENT,

CHILD

// Здесь можно добавить еще несколько классов

};

classParent

{

protected

intm_value;

public

Parent(intvalue)

m_value(value)

{

}

virtual~Parent(){}

virtualClassID getClassID(){returnPARENT;}

};

classChildpublicParent

{

protected

std::stringm_name;

public

Child(intvalue,std::stringname)

Parent(value),m_name(name)

{

}

std::string&getName(){returnm_name;}

virtualClassID getClassID(){returnCHILD;}

};

Parent*getObject(boolbReturnChild)

{

if(bReturnChild)

returnnewChild(1,»Banana»);

else

returnnewParent(2);

}

intmain()

{

Parent*p=getObject(true);

if(p->getClassID()==CHILD)

{

// Мы уже доказали, что p указывает на объект класса Child, поэтому никаких проблем здесь не должно быть

Child*ch=static_cast<Child*>(p);

std::cout<<«The name of the Child is: «<<ch->getName()<<‘\n’;

}

deletep;

return;

}

Но, если вы не уверены в успешности конвертации и не хотите заморачиваться с проверкой через виртуальные функции, вы можете просто использовать оператор dynamic_cast.

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

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

Теперь давайте разберемся с этим на примере.

// Java program demonstrate execution of static blocks and variables

import java.util.*;

public class VariableExample
{
// static variable
static int j = n();

// static block
static {
System.out.println("Inside the static block");
}

// static method
static int n() {
System.out.println("from n ");
return 20;
}

// static method(main !!)
public static void main(String[] args)
{
System.out.println("Value of j : "+j);
System.out.println("Inside main method");
}
}

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

Вывод:

from n
Inside the static block
Value of j: 20
Inside main method

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

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

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

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

Пример:

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

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

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

Примеры:

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

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

Примеры:

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

Static Class Objects

Static keyword works in the same way for class objects too. Objects declared static are allocated storage in static storage area, and have scope till the end of program.

Static objects are also initialized using constructors like other normal objects. Assignment to zero, on using static keyword is only for primitive datatypes, not for user defined datatypes.

constructor END destructor

You must be thinking, why was the destructor not called upon the end of the scope of condition, where the reference of object should get destroyed. This is because object was , which has scope till the program’s lifetime, hence destructor for this object was called when function exits.

Понижающее приведение vs. Виртуальные функции

Есть программисты, которые считают, что dynamic_cast — это зло и моветон. Они же советуют использовать виртуальные функции вместо оператора dynamic_cast.

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

   Если вы не можете изменить родительский класс, чтобы добавить в него свою виртуальную функцию (например, если родительский класс является частью Стандартной библиотеки С++). При этом, чтобы использовать понижающее приведение, в родительском классе должны уже присутствовать виртуальные функции.

   Если вам нужен доступ к чему-либо, что есть только в дочернем классе (например, к , которая существует только в дочернем классе).

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

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

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

public class NestedExample{
private static String str= "Edureka"
//Static class
static class MyNestedClass{
//non-static method
public void disp(){
System.out.println(str);
}
}
public static void main(String args[]){
NestedExample.MyNestedClass obj = new NestedExample.MyNestedClass();
obj.disp();

}

Когда вы выполняете приведенный выше код, ваш вывод выглядит так:

Переменные

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

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

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

Попрактикуемся

Чтобы убедиться, что вы не просто вызубрили теорию, а хорошо понимаете предмет, на собеседовании вам могут предложить задания вроде «что произойдёт при выполнении кода»

Разберём типовые задачи на понимание коллекций.

Задачи для ArrayList

Вариант попроще

Что будет напечатано после выполнения кода ниже:

Правильный ответ: test2:test4:test1:test4:test2:test3:

Объяснение

Элементы в ArrayList нумеруются начиная с нуля. Поэтому элемент с номером 1 — это test2.

Следующим действием мы добавляем строку «test4» в ячейку с индексом 1. При этом элементы с бо́льшим индексом сдвигаются вправо.

Вторая часть вывода (test4) показывает, что теперь по индексу 1 извлекается именно test4.

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

Вариант посложнее

Что будет выведено при выполнении кода:

Правильный ответ: 2:2

Объяснение

Первая часть понятна: добавили два элемента, поэтому размер списка равен двум. Остаётся вопрос: почему не был удалён «test1»?

Перед удалением элемента его нужно найти в списке. ArrayList и остальные коллекции, которые не используют алгоритмы хеширования, применяют для поиска метод equals().

Строки сравниваются по значению, поэтому «test3» не эквивалентно «test1» и «test2». А раз ни один элемент не соответствует критерию поиска, ничего не удалится — размер списка останется прежним.

Проверьте себя: подумайте, что произойдёт, если вместо

написать

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

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

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

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

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

Отдельные базы для shared_ptr- и unique_ptr-поведения

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

Начнем со случая shared_ptr-поведения, т.к. здесь меньше всего кода:

Ничего сложного: наследуемся от , наследуем все его конструкторы и определяем простую, неразрушающую реализацию .

Для случая unique_ptr-поведения кода побольше, хотя сложного в нем ничего нет:

Опять же, наследуемся от и наследуем у него нужные нам конструкторы (это конструктор по-умолчанию и инициализирующий конструктор). Но при этом определяем конструкторы и операторы копирования/перемещения в соответствии с логикой unique_ptr: копирование запрещаем, перемещение реализуем.

Также у нас здесь разрушающий метод .

Вот, собственно, все. Осталось только реализовать выбор между двумя этими базовыми классами…

Выбор между shared_ptr- и unique_ptr-поведением

Для выбора между shared_ptr- и unique_ptr-поведением потребуется следующая метафункция (метафункция она потому, что «работает» с типами в компайл-тайм):

Эта метафункция принимает оба параметра из списка параметров и в качестве результата (т.е. определения вложенного типа ) «возвращает» тип, от которого следует отнаследоваться. Т.е. либо , либо .

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

А для того, чтобы использовать метафункцию было проще, следом определим более короткое имя для нее:

Статические методы не имеют указателя *this

У статических методов есть две интересные особенности.

Во-первых, поскольку статические методы не привязаны к объекту, то они не имеют скрытого указателя *this! Здесь есть смысл, так как указатель *this всегда указывает на объект, с которым работает метод. Статические методы могут не работать через объект, поэтому и указатель *this не нужен.

Во-вторых, статические методы могут напрямую обращаться к другим статическим членам (переменным или функциям), но не могут напрямую обращаться к нестатическим членам. Это связано с тем, что нестатические члены принадлежат объекту класса, а статические методы — нет!

Оператор dynamic_cast и Ссылки

Хотя во всех примерах, приведенных выше, мы использовали динамическое приведение с указателями (что является наиболее распространенным), оператор dynamic_cast также может использоваться и со ссылками. Работа dynamic_cast со ссылками аналогична работе с указателями:

#include <iostream>
#include <string>

class Parent
{
protected:
int m_value;

public:
Parent(int value)
: m_value(value)
{
}

virtual ~Parent() {}
};

class Child: public Parent
{
protected:
std::string m_name;

public:
Child(int value, std::string name)
: Parent(value), m_name(name)
{
}

const std::string& getName() { return m_name; }
};

int main()
{
Child banana(1, «Banana»);
Parent &p = banana;
Child &ch = dynamic_cast<Child&>(p); // используем оператор dynamic_cast для конвертации ссылки класса Parent в ссылку класса Child

std::cout << «The name of the Child is: » << ch.getName() << ‘\n’;

return 0;
}

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
40
41

#include <iostream>
#include <string>

classParent

{

protected

intm_value;

public

Parent(intvalue)

m_value(value)

{

}

virtual~Parent(){}

};

classChildpublicParent

{

protected

std::stringm_name;

public

Child(intvalue,std::stringname)

Parent(value),m_name(name)

{

}

conststd::string&getName(){returnm_name;}

};

intmain()

{

Childbanana(1,»Banana»);

Parent&p =banana;

Child&ch =dynamic_cast<Child&>(p);// используем оператор dynamic_cast для конвертации ссылки класса Parent в ссылку класса Child

std::cout<<«The name of the Child is: «<<ch.getName()<<‘\n’;

return;

}

Поскольку в языке C++ не существует «нулевой ссылки», то dynamic_cast не может возвратить «нулевую ссылку» при сбое. Вместо этого, dynamic_cast генерирует исключение типа std::bad_cast (мы поговорим об исключениях чуть позже).

Общая база для хранения указателя

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

У этого шаблонного класса два параметра. Первый, Payload, задает тип, который должны использовать методы-getter-ы. Тогда как второй, Envelope, задает тип для intrusive_ptr. В случае, когда тип сообщения наследуется от оба эти параметра будут иметь одинаковое значение. А вот если сообщение не наследуется от , тогда в качестве Payload будет тип сообщения, а в качестве Envelope будет выступать .

Думаю, что в основном содержимое этого класса не вызывает вопросов

Но отдельно следует обратить внимание на две вещи

Во-первых, сам указатель, т.е. атрибут m_msg, определен в protected секции для того, чтобы классы наследники имели к нему доступ.

Во-вторых, для этого класса сам компилятор генерирует все необходимые конструкторы и операторы копирования/перемещения. И на уровне этого класса мы пока ничего не запрещаем.

Сигнатура метода

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

Статические методы можно использовать для реализации шаблона фабричный метод (Factory Method), который создает новые экземпляры содержащего его класса при каждом вызове. В данном примере фабричный метод создает экземпляр объекта , присваивает ему значения из массива и возвращает экземпляр:

Эту логику можно извлечь в отдельный класс, который известен как шаблон проектирования статическая фабрика (Static Factory):

Метод экземпляра или статический метод в Java?

  • Метод экземпляра получит прямой доступ к методам экземпляра и переменным.
  • Метод экземпляра будет обращаться к статическим переменным и статическим методам напрямую.
  • Статические методы будут обращаться к статическим переменным и методам напрямую.
  • Статические методы не могут напрямую обращаться к методам экземпляра и переменным экземпляра. И статический метод не может использовать это, так как нет экземпляра для «this», на который можно сослаться.

Оцени статью

Оценить

Средняя оценка / 5. Количество голосов:

Видим, что вы не нашли ответ на свой вопрос.

Помогите улучшить статью.

Спасибо за ваши отзыв!

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

В заключении
этого занятия я расскажу об еще одном ключевом слове final. Оно позволяет
задавать константы в языке Java. Например, если у поля cnt добавить это
ключевое слово, то дальнейшее изменение переменной cnt станет
невозможным:

private static final int cnt = ;

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

final int MAX_COORD = 10;

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

final int MAX_COORD;

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

Путь кодера

Подвиг 1. Объявите класс ShopItem для
представления продуктов в магазине с полями: id (идентификатор
– целое число), название товара, габариты, вес, цена. Поле id должно быть
уникальным для каждого объекта класса. Это следует реализовать через
статическую переменную, которая подсчитывает количество создаваемых
экземпляров.

Подвиг 2. Реализовать
класс Rect для описания
прямоугольника с полями: x1, y1, x2, y2 – координат
вершин верхнего правого и нижнего левого углов. Прописать два статических
метода для вычисления ширины и высоты прямоугольника. В качестве параметра этим
методам передавать ссылку на экземпляр класса Rect, для которого
выполняется вычисление.

Подвиг 3. Реализовать
класс Singleton, в котором
определить статический метод getInstance(). Этот метод
должен возвращать экземпляр класса, если он еще не создавался. Иначе,
возвращается ссылка на ранее созданный экземпляр. Также следует запретить
создание объектов класса Singleton напрямую через оператор new. (Полученная
реализация будет гарантировать существование только одного экземпляра класса в
процессе работы программы и, фактически, является примером известного паттерна singleton).

Видео по теме

#11 Концепция объектно-ориентированного программирования (ООП)

#12 Классы и создание объектов классов

#13 Конструкторы, ключевое слово this, инициализаторы

#14 Методы класса, сеттеры и геттеры, public, private, protected

#15 Пакеты, модификаторы конструкторов и классов

#16 Ключевые слова static и final

#17 Внутренние и вложенные классы

#18 Как делается наследование классов

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

#20 Модификаторы private и protected, переопределение методов, полиморфизм

#21 Абстрактные классы и методы

#22 Интерфейсы — объявление и применение

#23 Интерфейсы — приватные, статические и дефолтные методы, наследование интерфейсов

#24 Анонимные внутренние классы

#25 Перечисления (enum)

#26 Обобщения классов (Generics)

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

#28 Обобщенные интерфейсы, наследование обобщенных классов

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

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

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

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

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

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

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

Примеры:

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

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

Пример:

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

Отличие статических и нестатических переменных

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

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

Обычные переменные класса привязаны к объектам своего класса (экземплярам класса), статические переменные — к статическому объекту класса.

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

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

Пример:

Обращение к полю класса с использованием ссылки на объект класса

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

Пример:

Обращение к статическому полю класса не используя ссылку на объект класса

Устройство в памяти:

Допустим, у нас есть класс с 4 полями: два статических, а два — нет.

Сразу после загрузки класса

Когда Java-машина завершит загрузку класса , в памяти у нас будет наблюдаться такая картина:

После создания первого объекта

Если мы создадим объект класса , картинка станет такой

Обратите внимание, что хоть у объектов по две переменные, это разные переменные: у обычного объекта — обычные, у статического — статические. Нужно больше объектов

Нужно больше объектов

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

Обратите внимание: у каждого объекта есть собственная переменная age и name

Оператор dynamic_cast

В языке C++ оператор dynamic_cast используется именно для этого. Хотя динамическое приведение позволяет выполнять не только конвертацию указателей родительского класса в указатели дочернего класса, это является наиболее распространенным применением оператора dynamic_cast. Этот процесс называется приведением к дочернему типу (или «понижающим приведением типа»).

Использование dynamic_cast почти идентично использованию static_cast. Вот функция main() из вышеприведенного примера, где мы используем dynamic_cast для конвертации указателя класса Parent обратно в указатель класса Child:

int main()
{
Parent *p = getObject(true);

Child *ch = dynamic_cast<Child*>(p); // используем dynamic_cast для конвертации указателя класса Parent в указатель класса Child

std::cout << «The name of the Child is: » << ch->getName() << ‘\n’;

delete p;

return 0;
}

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

intmain()

{

Parent*p=getObject(true);

Child*ch=dynamic_cast<Child*>(p);// используем dynamic_cast для конвертации указателя класса Parent в указатель класса Child

std::cout<<«The name of the Child is: «<<ch->getName()<<‘\n’;

deletep;

return;

}

Результат:

Что такое статические методы в 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.

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

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

Adblock
detector