Кроме обычных методов и полей класс может иметь статические поля, методы, константы и инициализаторы. Кроме обычных полей, методов, свойств классы и структуры могут иметь статические поля, методы, свойства. Статические поля и методы ко всему классу, и для обращения к подобным полям и методам необязательно создавать объект класса.
Например, главный класс программы на Java должен иметь метод main, который является статическим:
public static void main(String[] args) {
}
Для объявления статических переменных, констант, методов и инициализаторов перед их объявлением указывается ключевое слово static.
Если обычные поля класса хранят состояние объекта, то статические поля хранят состояние всего класса. При создании объектов класса для каждого объекта создается своя копия нестатических обычных полей. А статические поля являются общими для всего класса и поэтому создаются в единственном виде.
Статическое поле определяется как и обычное, только перед типом поля указывается ключевое слово static. Например, рассмотрим статические поля в рамках класса Person, который представляет человека, и их применение:
class Program{
public static void main(String[] args) {
Person tom = new Person(41);
tom.checkAge(); // Сколько лет осталось до пенсии: 24
Person bob = new Person(68);
bob.checkAge(); // Уже на пенсии
// получение статического поля
System.out.println(Person.retirementAge); // 65
// изменение статического поля
Person.retirementAge = 67;
System.out.println(Person.retirementAge); // 67
}
}
class Person{
int age; // обычное поле
static int retirementAge = 65; // статическое поле
Person(int age){
this.age = age;
}
void checkAge()
{
if (age >= retirementAge)
System.out.println("Уже на пенсии");
else
System.out.printf("Сколько лет осталось до пенсии: %d\n", retirementAge - age);
}
}
В данном случае класс Person имеет два поля: age (хранит возраст человека) и retirementAge (хранит пенсионный возраст).
Однако поле retirementAge является статическим. Оно относится не к конкретному человеку, а ко всем людям. (В данном случае для
упрощения пренебрежем тем фактом, что в зависимости от пола и профессии пенсионный возраст может отличаться.) Таким образом, поле retirementAge относится
не к отдельную объекту и хранит значение НЕ отдельного объекта класса Person, а относится ко всему классу Person и хранит общее значение для всего класса.
Причем в самом классе мы можем использовать это поле как и любые другие. Так, в методе checkAge(), который поверяет пенсионный статус человека,
для проверки используем это поле:
if (age >= retirementAge)
Но если мы хотим обратиться к этому полю вне своего класса, то мы можем обращаться к этому полю по имени класса:
System.out.println(Person.retirementAge); Person.retirementAge = 67;
На уровне памяти для статических полей будет создаваться участок в памяти, который будет общим для всех объектов класса.
При этом память для статических переменных выделяется даже в том случае, если не создано ни одного объекта этого класса.
Статические инициализаторы предназначены для инициализации статических переменных, либо для выполнения таких действий, которые выполняются при создании самого первого объекта. Например, определим статический инициализатор:
public class Program{
public static void main(String[] args) {
Person tom = new Person(41);
Person bob = new Person(68);
tom.checkAge(); // Сколько лет осталось до пенсии: 24
bob.checkAge(); // Уже на пенсии
}
}
class Person{
int age; // обычное поле
static int retirementAge; // статическое поле
static{
retirementAge = 65;
System.out.println("Static initializer");
}
Person(int age){
this.age = age;
System.out.println("Constructor");
}
void checkAge()
{
if (age >= retirementAge)
System.out.println("Уже на пенсии");
else
System.out.printf("Сколько лет осталось до пенсии: %d\n", retirementAge - age);
}
}
Статический инициализатор определяется как обычный, только перед ним ставится ключевое слово static. В данном случае в статическом инициализаторе
мы устанавливаем начальное значение статического поля retirementAgeи выводим на консоль сообщение.
В самой программе создаются два объекта класса Person. Поэтому консольный вывод будет выглядеть следующим образом:
Static initializer Constructor Constructor Сколько лет осталось до пенсии: 24 Уже на пенсии
Стоит учитывать, что вызов статического инициализатора производится после загрузки класса и фактически до создания самого первого объекта класса.
Также статическими бывают константы, которые являются общими для всего класса.
public class Program{
public static void main(String[] args) {
double radius = 60;
System.out.printf("Radisu: %f \n", radius); // 60
System.out.printf("Area: %f \n", Math.PI * radius); // 188,4
}
}
class Math{
public static final double PI = 3.14;
}
Стоит отметить, что на протяжении всех предыдущих тем уже активно использовались статические константы. В частности, в выражении:
System.out.println("hello");
out как раз представляет статическую константу класса System. Поэтому обращение к ней идет без создания объекта класса System.
Статические методы определяют общее для всех объектов поведение, которое не зависит от конкретного объекта. В любой программе на Java мы уже сталкиваемся как минмум с одним статическим методом - это метод main, который автоматически запускается средой. И согласно правилам языка этот метод должен быть статическим:
public class Program{
// метод main должен быть статическим
public static void main(String[] args) {
// здесь действия программы
}
}
Для обращения к статическим методам также применяется имя класса. Например, определим следующую программу:
public class Program{
public static void main(String[] args) {
Person tom = new Person(41);
Person.checkStatus(tom); // Сколько лет осталось до пенсии: 24
}
}
class Person{
int age;
static int retirementAge = 65;
Person(int age){
this.age = age;
}
// статический метод
static void checkStatus(Person person)
{
if (person.age >= retirementAge)
System.out.println("Уже на пенсии");
else
System.out.printf("До пенсии осталось %d лет\n", retirementAge - person.age);
}
}
В данном случае в классе Person определен статический метод checkStatus(), который в качестве параметра принимает объект Person и проверяет его пенсионный статус.
Следует учитывать, что статические методы могут обращаться только к статическим членам класса. Обращаться к нестатическим методам и полям этого же класса внутри статического метода мы не можем.
Поэтому, например, чтобы вызвать в методе main другие методы, которые определены в одном классе с методом main, они также должны иметь модификатор
static:
public class Program{
public static void main(String[] args) {
sum(4, 5); // метод статический, поэтому мы можем его вызывать в методе main
// add(4, 5); // метод НЕстатический, поэтому мы НЕ можем его вызвать в методе main
}
static void sum(int a, int b){
System.out.printf("Сумма чисел %d и %d равна %d\n", a, b, a+b);
}
void add(int a, int b){
System.out.printf("Сумма чисел %d и %d равна %d\n", a, b, a+b);
}
}
Подчеркну, что в данном случае идет речь об обращении к методам и полям, которые определены в одном и том же классе.
Вообще методы определяются как статические, когда методы не затрагивают состояние объекта, то есть его нестатические поля и константы, и для вызова метода нет смысла создавать экземпляр класса. Например:
public class Program{
public static void main(String[] args) {
System.out.println(Operation.sum(45, 23)); // 68
System.out.println(Operation.subtract(45, 23)); // 22
System.out.println(Operation.multiply(4, 23)); // 92
}
}
class Operation{
static int sum(int x, int y){
return x + y;
}
static int subtract(int x, int y){
return x - y;
}
static int multiply(int x, int y){
return x * y;
}
}
В данном случае для методов sum, subtract, multiply не имеет значения, какой именно экземпляр класса Operation используется. Эти методы работают только с параметрами, не затрагивая состояние класса. Поэтому их можно определить как статические.