Статический контекст — это все, что принадлежит самому классу, а не его экземплярам (объектам); все, что обозначено ключевым словом static.
Статические переменные — это переменные, которые принадлежат классу, а не его экземплярам (объектам): одна на весь класс.
Статическая переменная создается один раз при загрузке класса в JVM и существует до завершения работы JVM.
public class Config {
// Статическая переменная инициализируется при загрузке класса
public static final String DATABASE_URL = "jdbc:mysql://localhost:3306/mydb";
public static int requestCount = 0;
// Существует до завершения работы JVM
}Статическая переменная доступа везде в том классе, где она объявлена:
public class MathUtils {
// Объявление и прямая инициализация статической инциализации
public static final double E = 2.71828;
// ...
double exp = Math.pow(E, x); // Где-то в классе
}Открытая статическая переменная также доступна в других классах:
public class MyLovelyMainClass {
public static void main(String[] args) {
// Доступ к статической переменной через имя класса, в котором она объявлена
double exp = Math.pow(MathUtils.E, x);
// ...
}
}public class Database {
public static Connection connection;
public static Properties config;
// Статический блок выполняется один раз при загрузке класса
static {
try {
config = new Properties();
config.load(new FileReader("config.properties"));
String url = config.getProperty("db.url");
String user = config.getProperty("db.user");
String password = config.getProperty("db.password");
connection = DriverManager.getConnection(url, user, password);
System.out.println("Database connection established");
} catch (Exception e) {
System.err.println("Failed to initialize database: " + e.getMessage());
}
}
}Статические методы — это методы, которые принадлежат классу, а не его экземплярам (объектам).
// Статический метод,
public static returnType methodName(parameters) {
// тело метода
}где returnType — это возвращаемый тип данных, а parameters — это список параметров.
Пример статического метода:
public static String greet(String name) {
return "Hello, " + name + "!";
}Инструкция перехода return используется для возврата управления из метода и передачи результата обратно вызывающему коду.
public static int add(int a, int b) {
return a + b; // возвращает сумму
}
public static boolean isEven(int number) {
return number % 2 == 0; // возвращает boolean
}Возвращаемое значение может отсутствовать, в этом случае возвращаемый тип данных указывается как void, а после ключевого слова return сразу следует завершение инструкции ;.
public void printIfPositive(int number) {
if (number <= 0) {
return; // досрочное завершение метода
}
System.out.println("Positive number: " + number);
}Вызов статистического метода из того же класса без указания имени класса:
public class Calculator {
public static int multiply(int a, int b) {
return a * b;
}
public static int power(int base, int exponent) {
int result = 1;
for (int i = 0; i < exponent; i++) {
result = multiply(result, base); // Вызов без Calculator.
}
return result;
}
}Вызов статистического метода из других классов через имя класса:
public class MathUtils {
public static int add(int a, int b) {
return a + b;
}
public static double calculateCircleArea(double radius) {
return Math.PI * radius * radius;
}
}
public class Main {
public static void main(String[] args) {
// Вызов статистических методов через имя класса
int sum = MathUtils.add(5, 3); // 8
double area = MathUtils.calculateCircleArea(2.5); // ~19.63
}
}Рекурсивный вызов:
public class RecursionDemo {
public static int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1); // Рекурсивный вызов
}
public static void main(String[] args) {
int result = factorial(5); // 120
System.out.println("5! = " + result);
}
}Передача аргументов примитивных типов по значению:
public class ByValueDemo {
public static void modifyPrimitive(int x) {
x = x * 2; // Изменяется копия, оригинал не меняется
System.out.println("Inside method: " + x); // 20
}
public static void main(String[] args) {
int number = 10;
modifyPrimitive(number);
System.out.println("After method: " + number); // 10 (не изменился)
}
}Передача аргументов ссылочных типов по значению ссылки:
public class ByReferenceDemo {
public static void modifyArray(int[] arr) {
arr[0] = 100; // Меняет содержимое массива
System.out.println("Inside method: " + Arrays.toString(arr)); // [100, 2, 3]
}
public static void reassignArray(int[] arr) {
arr = new int[]{10, 20, 30}; // Меняет локальную ссылку
System.out.println("Reassigned: " + Arrays.toString(arr)); // [10, 20, 30]
}
public static void main(String[] args) {
int[] numbers = {1, 2, 3};
modifyArray(numbers);
System.out.println("After modify: " + Arrays.toString(numbers)); // [100, 2, 3]
reassignArray(numbers);
System.out.println("After reassign: " + Arrays.toString(numbers)); // [100, 2, 3] (не изменился)
}
}public class VarargsDemo {
public static int sum(int... numbers) {
// Сюда передается массив numbers типа int[]
int total = 0;
for (int num : numbers) {
total += num;
}
return total;
}
public static void main(String[] args) {
int total = sum(1, 2, 3, 4, 5); // 15
int empty = sum(); // 0
}
}public class FinalDemo {
public static void processFinal(final int x, final String str) {
// x = 10; // ОШИБКА: нельзя изменить final параметр
// str = "new"; // ОШИБКА: нельзя изменить ссылку
System.out.println("x: " + x + ", str: " + str);
}
public static void main(String[] args) {
processFinal(5, "Hello");
}
}public class OverloadDemo {
public static void print(int number) {
System.out.println("Integer: " + number);
}
public static void print(double number) {
System.out.println("Double: " + number);
}
public static void print(String text) {
System.out.println("String: " + text);
}
public static void print(String text, int times) {
for (int i = 0; i < times; i++) {
System.out.println(text);
}
}
public static void main(String[] args) {
print(10); // Integer: 10
print(3.14); // Double: 3.14
print("Hello"); // String: Hello
print("Java", 3); // Java Java Java
}
}Импорт отдельных статических переменных и методов:
import static java.lang.Math.PI; // Статический импорт переменной
import static java.lang.Math.pow; // Статический импорт метода
public class MathExample {
public static void main(String[] args) {
double radius = 5.0;
// Доступ к статическим членам класса Math без имени класса
double area = PI * pow(radius, 2);
}
}Импорт всех открытых статических членов класса:
import static java.lang.Math.*; // Статический импорт
public class MathExample {
public static void main(String[] args) {
double radius = 5.0;
// Доступ к статическим членам класса Math без имени класса
double area = PI * pow(radius, 2);
}
}Локальные переменные — это переменные, объявленные внутри методов, конструкторов или блоков кода. Они создаются при входе в блок и уничтожаются при выходе из него.
Область видимости может быть ограничена телом метода:
public void exampleMethod() {
int localVar = 10; // Локальная переменная метода
System.out.println(localVar);
}Область видимости параметров метода:
public void methodWithParams(int param) { // param — это локальная переменная
System.out.println(param);
}
// param недоступен здесьОбласть видимости может быть ограничена блоком кода (if, for, while и т. д.)
public void blockExample() {
if (true) {
int blockVar = 20; // Локальная переменная блока
System.out.println(blockVar);
}
// blockVar недоступна здесь - ошибка компиляции!
// System.out.println(blockVar);
}Текстовые блоки — это многострочные строковые литералы.
Текстовый блок задает одно строковое значение, заключенное между двумя последовательностями из трех двойных кавычек """.
String query = """
SELECT *
FROM users
WHERE age > 18
AND city = 'New York'
""";Форматирование строк позволяет создавать строки по заданным шаблонам, вставляя в них динамические значения с помощью специальных спецификаторов формата, которые определяют тип и формат данных.
Основные спецификаторы формата:
%s— строка%d— целое число%f— число с плавающей точкой%n— перевод строки%%— символ процента
Форматирование с использованием статического метода String.format():
// Форматирование чисел
String price = String.format("Цена: %.2f руб.", 123.4567); // "Цена: 123.46 руб."
// Форматирование даты
String date = String.format("Дата: %tD", new Date()); // "Дата: 12/25/2024"
// Несколько аргументов
String info = String.format("Имя: %s, Возраст: %d, Рост: %.2f", "Анна", 25, 1.75);
// С ведущими нулями
String padded = String.format("Номер: %05d", 42); // "Номер: 00042"
// С разделителями тысяч
String withCommas = String.format("Население: %,d", 1000000); // "Население: 1,000,000"
// Округление до 2 знаков
String num = String.format("Число: %,.2f", 1234567.8912); // "Число: 1,234,567.89"
// Выравнивание по правому краю (по умолчанию)
String right = String.format("|%10s|", "текст"); // "| текст|"
// Выравнивание по левому краю
String left = String.format("|%-10s|", "текст"); // "|текст |"
// Комбинированное форматирование
String combined = String.format("|%-15s|%8.2f|%6d|", "Продукт", 123.45, 10);Вывод в командную строку c форматированием:
System.out.printf("Привет, %s!%n", "Мир");
System.out.printf("Число: %d, Дробное: %.3f%n", 42, 3.14159);Форматирование с использованием класса Formatter:
Formatter formatter = new Formatter();
formatter.format("Language: %-20s rate: %03d", "Java", 5);
String result = formatter.toString();
formatter.close();Текстовые блоки с форматированием:
String html = """
<html>
<head>
<title>%s</title>
</head>
<body>
<h1>%s</h1>
</body>
</html>
""".formatted("Мой заголовок", "Ура, Привет!");Метод compareTo сравнивает две строки лексикографически
String s1 = "aaa";
String s2 = "bbb";
// Сравнить две строки с учетом регистра
if (s1.compareTo(s2) > 0)
System.out.println("s1 больше s2 (правее лексикографически)");
else if (s1.compareTo(s2) < 0)
System.out.println("s1 меньше s2 (левее лексикографически)");
else
System.out.println("s1 равна s2");Метод compareToIgnoreCase сравнивает две строки лексикографически без учета регистра
// Сравнить две строки без учета регистра
if (s1.compareToIgnoreCase(s2) > 0)
System.out.println("s1 больше s2 (правее лексикографически)");
else if (s1.compareToIgnoreCase(s2) < 0)
System.out.println("s1 меньше s2 (левее лексикографически)");
else
System.out.println("s1 равна s2");String s = "Hello planet earth, you are a great planet";
// Привести строку к нижнему регистру
s = s.toLowerCase();
// Разделить строку на токены
String tokens[] = s.split(" ");
// Упорядочить массив строк лексикографически
Arrays.sort(tokens);
// Напечатать массив строк
System.out.println(Arrays.toString(tokens));Регулярные выражения — это механизм для поиска и замены текста; шаблоны для сопоставления последовательностей символов в строках.
Регулярные выражения позволяют проверить соответствие строки шаблону.
// Скомпилировать регулярное выражение
Pattern p = Pattern.compile("a*b");
// Сопоставить заданную строку с регулярным выражением
String s = "aaaab";
Matcher m = p.matcher(s);
boolean b = m.matches();Альтернативно можно использовать метод matches класса Pattern.
boolean b1 = Pattern.matches("a*b", "aaaab");или метод matches объектов типа String
boolean b2 = "aaaab".matches("a*b");Регулярные выражения позволяют делать замены в тексте.
// Удалить лишние пробелы
String s = "bla bla bla bla bla bla";
s = s.replaceAll("\\s+", " ");
System.out.println(s);Выполнить токенизацию с учетом дополнительных символов-разделителей.
// Разделить строку на токены
String s = "bla, bla. bla; bla bla bla...";
String tokens[] = s.replaceAll("[.,;]", "").split("\\s+");
System.out.println(Arrays.toString(tokens));Пример валидации телефонных номеров с помощью регулярных выражений.
String phones[] = {
"+7-902-111-22-33",
"8 902 111 2233",
"+7 (495) 123-22-33",
"+1 000 000 00 00"
};
for (String phone: phones) {
String replaced = phone.replaceAll("[-() ]", "");
boolean validPhone = Pattern.matches("(\\+7|8)\\d{10}", replaced);
if (validPhone)
System.out.printf("\"%s\" is a valid phone number%n", phone);
else
System.out.printf("\"%s\" is not a valid phone number%n", phone);
}Объекты класса StringBuilder представляют собой изменяемые последовательности символов.
Создание последовательностей символов StringBuilder:
StringBuilder sb1 = new StringBuilder(); // емкость по умолчанию: 16 символов
StringBuilder sb2 = new StringBuilder(100); // начальная емкость: 100 символов
StringBuilder sb3 = new StringBuilder("Hello"); // содержимое: "Hello"В отличие от обычных строк типа String, которые являются неизменяемыми, последовательности символов StringBuilder могут модифицироваться без создания новых объектов.
Добавление содержимого:
StringBuilder sb = new StringBuilder();
sb.append("1"); // "1"
sb.append("2"); // "12"
sb.append(3); // "123"Вставка в указанную позицию:
StringBuilder sb = new StringBuilder("Hello World");
sb.insert(5, " Beautiful"); // "Hello Beautiful World"
sb.insert(0, "Oh, "); // "Oh, Hello Beautiful World"Удаление:
StringBuilder sb = new StringBuilder("Hello World");
sb.delete(5, 11); // "Hello" (удаляет с 5 по 10 индекс)
sb.deleteCharAt(0); // "ello" (удаляет первый символ)Замена части строки:
StringBuilder sb = new StringBuilder("Hello World");
sb.replace(6, 11, "Java"); // "Hello Java" (замена с 6 по 10 индекс)
// Замена одного символа
sb.setCharAt(0, 'h'); // "hello Java"Обращение строки:
StringBuilder sb = new StringBuilder("кот");
sb.reverse(); // "ток"Отладка — это процесс обнаружения и исправления ошибок в программе за счет остановки выполнения программы в определенной точке, анализу состояния программы и, при необходимости, пошаговому её выполнению.
Отладчик — это инструмент, позволяющий эффективно выполнять отладку, предоставляя представление о внутренних процессах программы.
Отладка приложения включает следующие шаги:
- Установка точек останова (щелчок мыши по номеру строки), т. е. тех строк кода, на которых программа будет приостановлена отладчиком, чтобы вы могли проверить ее состояние.
- Запуск программы в режиме отладки (Shift + F9).
- Анализ состояния программы: при достижении точки останова выполнение программы приостанавливается, подсвечивается строка, на которой она остановилась, и открывается окно отладки.
- Выполнение программы шаг за шагом: Шаг с обходом (F8) позволяет выполнить строку коду, не входя в вызываемый метод при его наличии. Шаг с заходом (F7) позволяет войти в вызываемый метод при его наличии. Шаг с возвратом (Shift + F8) позволяет выйти из текущего метода и вернуться в вызывающий его код. Выполнение программы до курсора (Alt + F9).
- Остановка отладки (Ctrl + F2) или возобновление выполнения программы в обычном режиме (F9).
Задание 4-1 — 1 балл
- Реализовать публичный класс верхнего уровня
Validatorс открытыми статическими методами валидации строковых выражений следующих видов: адресов электронной почты, URL-ссылок, IP-адресов, номеров телефонов, СНИЛС, серий и номеров паспортов, дат и времени. Каждый статический метод должен работать с единственным видом строковых выражений. - Реализовать публичный класс верхнего уровня
Normalizerс открытыми статическими методами форматирования строковых выражений следующих видов: номеров телефонов, СНИЛС, серий и номеров паспортов, дат и времени. Каждый статический метод должен работать с единственным видом строковых выражений. Перед форматированием должна выполняться валидация строкового выражения с помощью статических методов классаValidator. В случае если строковое выражение валидно, то оно приводится (форматируется) к эталонному представлению и в таком виде возвращается из метода. Например, исходное номер телефона подается в следующем виде "89996663322", сперва необходимо убедиться это валидная запись, затем выполнить форматирование и вернуть эталонное значение "+7-999-666-33-22". - Реализовать отдельный Main-класс, который будет считывать пользовательские строковые выражения с командной строки и вызывать соответствующие статические методы валидации класса
Validatorи статические методы форматирования классаNormalizer. КлассыValidatorиNormalizerне должны быть Main-классами. - Используемые методы классов
ValidatorиNormalizerдолжны быть импортированы в Main-класс через статический импорт. - Продемонстрировать работу отладчика в IDE. Установить точку останова в вашем коде. Запустить отладчик. Показать какие значения имеют локальные переменные в момент останова. Сделать один или несколько шагов так, продемонстрировать изменение значений локальных переменных при пошаговом выполнении кода. Установить курсор в некоторой строке вашего кода и выполнить код до этой строки; показать какие значения имеют локальные переменные в этот момент. Возобновить выполнение программы в обычном режиме.
Задание 4-2 — 1 балл
- Найти расстояние Левенштейна между двух слов W1 и W2, т. е. минимальное количество операций, необходимое для преобразования W1 в W2. Разрешены три операции редактирования слова: вставка символа, удаление символа, замена символа. Например, преобразование W1 = "intention" в W2 = "execution" требует 5 следующих операций: "intention" -> "inention" (удалить 't'); inention -> enention (заменить 'i' на 'e'); "enention" -> "exention" (заменить 'n' на 'x'); "exention" -> "exection" (заменить 'n' на 'c'); "exection" -> "execution" (вставить 'u').
- Реализовать ввод слов W1 и W2 через командную строку.
- Реализовать вывод применяемых операций редактирования слова W1 в слово W2 в командную строку.
- Реализовать вывод расстояния Левенштейна между слов W1 и W2 в командную строку.
- Вычисление расстояния Левенштейна должно выполняться только статическими методами.
Задание 4-3 — 2 балла
- Реализовать игру "Жизнь" (Дж. Конвея) с помощью матриц (двумерных массивов).
- Исходный код должен быть организован в виде единственного Main-класса с несколькими статическими методами, реализующими действия игры.
- Сопроводить исходный код Javadoc-комментариями и сгенерировать HTML-документацию с помощью команды javadoc.
- Собрать проект в виде запускаемого JAR-файла и продемонстрировать его работу.
- Какую область видимости имеет какая-либо локальная переменная в вашем коде?
- Какую область видимости имеет какая-либо статическая переменная в вашем коде?
- Какое время жизни имеет статическая переменная?
- Какое время жизни имеет какая-либо локальная переменная в вашем коде?
- Какие статические импорты есть в вашей программе?
- Какие вызовы статических методов выполняются в вашей программе?
- Какие статические переменные есть в вашем коде?
- Какие локальные переменные есть в вашем коде?
- Как передаются параметры в ваши методы: копия значения или ссылка на значение?
- Объясните сигнатуру какого-либо метода в вашем коде?
- Выделите объявление какого-либо метода в вашем коде?
- Выделите тело какого-либо метода в вашем коде?
- Какой возвращаемый тип данных имеет тот или иной метод в вашем коде?
- Является ли тот или иной метод в вашем коде открытым (публичным)?
- Используете ли вы перегрузку статических методов в вашем коде?
- Есть ли рекурсивные вызовы методов в вашем коде?
- Есть методы с переменным числом аргументов в вашем коде?
- Как можно вызвать какой-либо написанный вами статический метод?
- Есть в вашем коде вызовы какого-либо написанного вами статического метода?
- Какие статические члены из написанных вами можно импортировать?
- Какие спецификаторы формата вы используете при форматировании строк?
- Объясните какой-либо шаблон форматирования строк в вашем коде?
- Какие регулярные выражения вы используете в вашем коде?
- Объясните какое-либо регулярное выражение в вашем коде?
- Используете ли вы текстовые блоки в вашем коде?
- Какие инструкции перехода есть в вашем коде?
- В какую строку вашего кода будет передано управление при завершении того или иного метода, написанного вами?
- Какой тип данных имеет тот или иной параметр вашего метода?
- Какую область видимости имеют параметры ваших методов?
- Меняется ли состояние строк в вашем коде?