static: поля и методы
Статические члены принадлежат классу, а не отдельным объектам. Вы уже пользуетесь ими: Math.PI, Integer.parseInt. Сегодня разберём, как это работает.
Что такое static
static означает «статический», то есть принадлежащий КЛАССУ, а не конкретному объекту. Когда вы создаёте объект через new, у каждого объекта свои поля. Но статические поля — ОДНИ на всех.
Аналогия: представьте classroom教室 в школе. У каждого ученика есть свой учебник (нестатическое поле). Но доска в教室е одна на всех (статическое поле). Доска принадлежит教室у, а не конкретному ученику. Учебники можно переписать, но доска общая.
public class Dog {
String name;
static int count;
Dog(String name) {
this.name = name;
count++;
}
}
Dog d1 = new Dog("Рекс");
Dog d2 = new Dog("Бобик");
Dog d3 = new Dog("Мухтар");
System.out.println(d1.count); // 3
System.out.println(d2.count); // 3
System.out.println(Dog.count); // 3 — лучше обращаться через имя класса!
Обратите внимание: поле count увеличивается каждый раз при создании нового объекта. И оно одинаковое для всех трёх собак. Потому что оно статическое — принадлежит классу Dog, а не конкретной собаке.
Статические vs нестатические поля
Давайте сравним:
public class Student {
String name; // нестатическое — у каждого студента своё
int grade; // нестатическое — у каждого своё
static String school; // статическое — одно на всех студентов
static int totalStudents; // статическое — общее число
Student(String name, int grade) {
this.name = name;
this.grade = grade;
totalStudents++;
}
}
Student.school = "Школа №42";
Student s1 = new Student("Алиса", 10);
Student s2 = new Student("Борис", 11);
Student s3 = new Student("Варвара", 10);
System.out.println(s1.name); // Алиса
System.out.println(s2.name); // Борис
System.out.println(s1.school); // Школа №42
System.out.println(s2.school); // Школа №42
System.out.println(Student.school); // Школа №42
System.out.println(Student.totalStudents); // 3
Нестатические поля (name, grade) — у каждого объекта свои значения. Статические поля (school, totalStudents) — одинаковые для всех объектов.
В памяти это выглядит так: каждый объект Student в куче имеет свои name и grade. А поле school и totalStudents — одна копия на всех, хранящаяся отдельно.
Статические методы
Статические методы можно вызывать БЕЗ создания объекта. Они не привязаны к конкретному экземпляру.
public class MathUtils {
public static int sum(int a, int b) {
return a + b;
}
public static boolean isEven(int n) {
return n % 2 == 0;
}
public static long factorial(int n) {
long result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
return result;
}
public static double average(int a, int b, int c) {
return (a + b + c) / 3.0;
}
}
// Вызов без объекта:
int s = MathUtils.sum(5, 3); // 8
boolean even = MathUtils.isEven(4); // true
long f = MathUtils.factorial(5); // 120
double avg = MathUtils.average(10, 20, 30); // 20.0
Вы уже использовали статические методы: Math.sqrt(), Math.max(), Integer.parseInt(), System.out.println(). Все они статические.
Внутри статического метода НЕЛЬЗЯ использовать this и обращаться к нестатическим полям. Потому что статический метод не привязан ни к какому объекту — «this» просто не существует.
public class Example {
int value = 10;
public static void doSomething() {
// System.out.println(this); // Ошибка!
// System.out.println(value); // Ошибка!
System.out.println("Статический метод работает");
}
}
Math — класс со статическими методами
Класс Math — наглядный пример статических методов. Создавать объект Math не нужно — все методы статические:
public class MathDemo {
public static void main(String[] args) {
System.out.println(Math.PI); // 3.141592653589793
System.out.println(Math.E); // 2.718281828459045
System.out.println(Math.sqrt(16)); // 4.0
System.out.println(Math.pow(2, 10)); // 1024.0
System.out.println(Math.abs(-42)); // 42
System.out.println(Math.max(10, 20)); // 20
System.out.println(Math.min(10, 20)); // 10
System.out.println(Math.round(3.7)); // 4
System.out.println(Math.ceil(3.2)); // 4.0
System.out.println(Math.floor(3.7)); // 3.0
System.out.println(Math.sin(Math.PI / 2)); // 1.0
System.out.println(Math.log(Math.E)); // 1.0
}
}
Все эти методы статические. Вы вызываете их через имя класса: Math.sqrt(16), а не new Math().sqrt(16).
Поле Math.PI — это статическая константа: public static final double PI = 3.141592653589793;
public static final — константы
Когда вы комбинируете static и final, получаете константу — значение, которое нельзя изменить:
public class Config {
public static final String APP_NAME = "Моё приложение";
public static final int MAX_USERS = 1000;
public static final double PI = 3.14159;
public static final String VERSION = "1.0.0";
public static final int MAX_LOGIN_ATTEMPTS = 3;
}
System.out.println(Config.APP_NAME); // Моё приложение
// Config.APP_NAME = "Другое"; // Ошибка! final нельзя изменить
Соглашение об именовании: константы пишутся ЗАГЛАВНЫМИ БУКВАМИ с подчёркиванием: MAX_USERS, PI, VERSION.
Почему public static final — это константа?
public— доступна отовсюдуstatic— принадлежит классу (не нужно создавать объект)final— значение нельзя изменить после присваивания
import static
import static позволяет импортировать статические成员, чтобы не писать имя класса каждый раз:
// Без static import:
double x = Math.sqrt(16);
double y = Math.PI * 2;
int max = Math.max(10, 20);
System.out.println(Math.abs(-5));
// С static import:
import static java.lang.Math.*;
import static java.lang.System.out;
public class Test {
public static void main(String[] args) {
double x = sqrt(16); // 4.0
double y = PI * 2; // 6.28...
int max = max(10, 20); // 20
out.println(abs(-5)); // 5
}
}
Становится короче и читабельнее, особенно когда вы часто используете методы одного класса.
Статический инициализатор
Блок static выполняется ОДИН РАЗ при первом обращении к классу:
public class Database {
private static Connection connection;
private static List<String> tables;
static {
System.out.println("Инициализация базы данных...");
connection = new Connection("jdbc:mysql://localhost/mydb");
tables = new ArrayList<>();
tables.add("users");
tables.add("products");
tables.add("orders");
System.out.println("База данных готова!");
}
public static Connection getConnection() {
return connection;
}
public static List<String> getTables() {
return tables;
}
}
// При первом обращении:
Database.getConnection(); // "Инициализация..." → "База данных готова!"
Database.getTables(); // Сразу работает, инициализация уже была
Статический инициализатор полезен для настройки ресурсов, которые нужны всем объектам класса: подключение к БД, чтение конфигурации, загрузка данных.
Полезный пример: счётчик объектов
public class User {
private String name;
private int id;
private static int nextId = 1;
private static int totalUsers = 0;
public User(String name) {
this.name = name;
this.id = nextId++;
totalUsers++;
}
public String getName() {
return name;
}
public int getId() {
return id;
}
public static int getTotalUsers() {
return totalUsers;
}
public void display() {
System.out.printf("User #%d: %s%n", id, name);
}
}
User u1 = new User("Алиса");
User u2 = new User("Борис");
User u3 = new User("Варвара");
u1.display(); // User #1: Алиса
u2.display(); // User #2: Борис
u3.display(); // User #3: Варвара
System.out.println("Всего пользователей: " + User.getTotalUsers()); // 3
Поле nextId статическое — оно одно на всех. Каждый раз при создании пользователя id увеличивается. Поле totalUsers тоже статическое — считает общее число пользователей.
Статические методы и утилиты
Статические методы удобны для утилитных функций, которые не зависят от состояния объекта:
public class StringUtils {
public static boolean isNullOrEmpty(String s) {
return s == null || s.isEmpty();
}
public static boolean isNumeric(String s) {
if (s == null || s.isEmpty()) return false;
for (int i = 0; i < s.length(); i++) {
if (!Character.isDigit(s.charAt(i))) return false;
}
return true;
}
public static String capitalize(String s) {
if (s == null || s.isEmpty()) return s;
return Character.toUpperCase(s.charAt(0)) + s.substring(1).toLowerCase();
}
public static String repeat(String s, int times) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < times; i++) {
sb.append(s);
}
return sb.toString();
}
public static String reverse(String s) {
return new StringBuilder(s).reverse().toString();
}
}
System.out.println(StringUtils.isNullOrEmpty("")); // true
System.out.println(StringUtils.isNumeric("12345")); // true
System.out.println(StringUtils.capitalize("hello")); // Hello
System.out.println(StringUtils.repeat("abc", 3)); // abcabcabc
System.out.println(StringUtils.reverse("Java")); // avaJ
Когда использовать static
Используйте static когда:
- Значение должно быть ОДНО на все объекты (счётчик, конфигурация)
- Метод не зависит от состояния конкретного объекта (математические операции, утилиты)
- Вы хотите вызвать метод без создания объекта (
Math.sqrt()) - Константа (
public static final)
Не используйте static когда:
- Значение должно быть разным для каждого объекта (имя, возраст, баланс)
- Метод работает с полями конкретного объекта
- Нужно использовать
this
Статические константы в реальных проектах
В реальных проектах статические константы используются повсюду:
public class AppConfig {
public static final String APP_NAME = "МойБанк";
public static final String VERSION = "2.1.0";
public static final int MAX_LOGIN_ATTEMPTS = 3;
public static final int SESSION_TIMEOUT_MINUTES = 30;
public static final double DEFAULT_INTEREST_RATE = 0.05;
public static final String CURRENCY = "RUB";
public static final String DATE_FORMAT = "dd.MM.yyyy";
public static final String ENCODING = "UTF-8";
}
public class Validation {
public static final int MIN_PASSWORD_LENGTH = 8;
public static final int MAX_PASSWORD_LENGTH = 64;
public static final int MIN_NAME_LENGTH = 2;
public static final int MAX_NAME_LENGTH = 100;
public static final String EMAIL_REGEX = "^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,}$";
}
public class MathConstants {
public static final double PI = 3.141592653589793;
public static final double E = 2.718281828459045;
public static final double GOLDEN_RATIO = 1.618033988749895;
public static final double SQRT_2 = 1.4142135623730951;
}
Константы удобны тем, что:
- Их можно изменить в одном месте, и они обновятся везде
- Они самодокументированы — имя говорит само за себя
- Они неизменяемы — защита от случайных изменений
Статические методы vs методы экземпляра
Давайте сравним два типа методов:
public class Circle {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
// Метод экземпляра — работает с полем radius
public double getArea() {
return Math.PI * radius * radius;
}
// Статический метод — не зависит от radius
public static double calculateArea(double radius) {
return Math.PI * radius * radius;
}
// Метод экземпляра — использует this
public void display() {
System.out.printf("Круг с радиусом %.1f, площадь %.1f%n",
radius, getArea());
}
// Статический метод — не использует this
public static Circle createUnit() {
return new Circle(1.0);
}
}
// Метод экземпляра:
Circle c = new Circle(5);
double area = c.getArea(); // 78.5
// Статический метод:
double area2 = Circle.calculateArea(5); // 78.5
// Создание объекта через статический метод:
Circle unit = Circle.createUnit();
Метод экземпляра getArea() работает с полем radius конкретного объекта. Статический метод calculateArea() принимает radius как параметр — он не зависит ни от какого объекта.
Наследование и static
Статические члены наследуются, но не переопределяются:
public class Animal {
protected String name;
static int count = 0;
Animal(String name) {
this.name = name;
count++;
}
static int getCount() {
return count;
}
}
public class Dog extends Animal {
Dog(String name) {
super(name);
}
}
Dog d1 = new Dog("Рекс");
Dog d2 = new Dog("Бобик");
System.out.println(Dog.getCount()); // 2
System.out.println(Animal.getCount()); // 2 — та же переменная!
Поле count — одна копия на весь класс Animal. Dog наследует его, но не создаёт свою копию.
Типичные ошибки
public class StaticMistakes {
private int value;
public static void wrong1() {
// System.out.println(value); // Ошибка! Нестатическое поле
}
public static void wrong2() {
// System.out.println(this); // Ошибка! Нет this в static
}
public void correct() {
System.out.println(value); // ОК — нестатический метод
}
}
Итоги урока
staticпринадлежит классу, а не объектам.- Статические поля — одна копия на все экземпляры.
- Статические методы вызываются без объекта (Math.sqrt()).
- Внутри static нельзя использовать
thisи нестатические поля. public static final= константа (UPPER_CASE).import staticпозволяет опустить имя класса.- Статический инициализатор выполняется один раз при загрузке класса.
Следующий урок: Пакеты и организация кода — package, import, структура проекта.
Тест: static — поля и методы
10 вопросов