Skip to content

Latest commit

 

History

History
660 lines (540 loc) · 33 KB

File metadata and controls

660 lines (540 loc) · 33 KB

Занятие 4. Статический контекст

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

Статический контекст — это все, что принадлежит самому классу, а не его экземплярам (объектам); все, что обозначено ключевым словом 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

Инструкция перехода 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] (не изменился)
    }
}

Переменное количество аргументов (Varargs)

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(); // "ток"

Отладка приложений

Отладка — это процесс обнаружения и исправления ошибок в программе за счет остановки выполнения программы в определенной точке, анализу состояния программы и, при необходимости, пошаговому её выполнению.

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

Отладка приложения включает следующие шаги:

  1. Установка точек останова (щелчок мыши по номеру строки), т. е. тех строк кода, на которых программа будет приостановлена отладчиком, чтобы вы могли проверить ее состояние.
  2. Запуск программы в режиме отладки (Shift + F9).
  3. Анализ состояния программы: при достижении точки останова выполнение программы приостанавливается, подсвечивается строка, на которой она остановилась, и открывается окно отладки.
  4. Выполнение программы шаг за шагом: Шаг с обходом (F8) позволяет выполнить строку коду, не входя в вызываемый метод при его наличии. Шаг с заходом (F7) позволяет войти в вызываемый метод при его наличии. Шаг с возвратом (Shift + F8) позволяет выйти из текущего метода и вернуться в вызывающий его код. Выполнение программы до курсора (Alt + F9).
  5. Остановка отладки (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-файла и продемонстрировать его работу.

Вопросы

  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. Меняется ли состояние строк в вашем коде?

Дополнительные ресурсы

  1. Tutorial: Debug your first Java application
  2. Debug code
  3. Class Formatter
  4. Class Pattern
  5. Онлайн редактор регулярных выражений
  6. Расстояние Левенштейна
  7. Игра "Жизнь"