JavaRush /Java блог /Random UA /Кава-брейк #88. Сила метаданих: як працювати зі спагетті-...

Кава-брейк #88. Сила метаданих: як працювати зі спагетті-кодом. Складання сміття в Java - як вона працює і в чому її переваги

Стаття з групи Random UA

Сила метаданих: як працювати зі спагетті-кодом

Джерело: Hackernoon Ми всі намагаємося використовувати загальні підходи та відомі шаблони для створення програми з мінімальними зусиллями та максимальною віддачею. У нас є чудові бібліотеки та потужні фреймворки, які виконують рутинні операції за нас. Все це ми використовуємо для того, щоб зосередитись тільки на бізнес-логіці. Однак ця погоня досить часто призводить до спагетті-коду, особливо коли йдеться про реалізацію функції без готового рішення для неї. У цій статті я хочу поділитися з вами одним потужним інструментом, який, на мій досвід, цінують не всі розробники. Цей інструмент є в більшості мов програмування, і він часто використовується в багатьох фреймворках — анотації. Кава-брейк #88.  Сила метаданих: як працювати зі спагетті-кодом.  Складання сміття в Java - як вона працює і в чому її переваги - 1

Ви любите спагетті?

Давайте розглянемо приклад, з яким я зіткнувся кілька років тому. Мені потрібно було зробити синтаксичний аналіз електронної таблиці Excel, щоб помістити проаналізовані дані до бази даних. Також я хотів зібрати частину даних із бази даних та створити електронну таблицю. Для реалізації я використав відому Java-бібліотеку — Apache POI. API бібліотеки полегшує роботу, оскільки дозволяє вручну створювати аркуш, рядок, комірку та інші елементи. Це дуже добре, але коли необхідно генерувати різні електронні таблиці Excel, код стає абсолютно нечитаним та непідтримуваним. У результаті, як це зазвичай буває, перша версія програми виходить просто жахливою. Реалізація складалася з класу даних, який представляв рядок з усіма полями, необхідні синтаксичного аналізу. Також був парсер, в якому поля Excel аналізувалися осередок за осередком і поміщалися у новостворений екземпляр класу даних. Спочатку програма працювала добре і робила те, що від неї вимагалося. Проблеми почалися, коли настав час вносити якісь модифікації; код не читали. Навіть я, який написав цей код, не міг знайти місця для розміщення нових рядків для реалізації нової необхідної мені функції.

Порятунок в інструкціях

Врятували додаток від цього спагетті коду анотації. Щоб позбутися коду, що не підтримується, мені потрібно було перенести логіку визначення того, який стовпець слід аналізувати, який тип даних міститься в осередку і все інше в інше місце. Для цього я створив інструкцію, в якій вказав ім'я стовпця для кожного поля класу. В анотації я також додав змінну, за допомогою якої можна вибирати колір та шрифт комірки. Тим самим код у класі синтаксичного аналізу було значно скорочено. Лише один обробник динамічно створював електронну таблицю за параметрами, взятими з анотацій. То була перемога. Потім, щоб внести будь-які зміни до програми, мені просто потрібно було створити клас з інструкціями. Рішення нагадувало бібліотеку Jackson, яка аналізує JSON за допомогою анотацій, і, я думаю, немає необхідності розповідати, наскільки зручною є Jackson або аналогічні бібліотеки.
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnExcel {

    String name() default "";

    int position();

    ExcelColumnDataFormat cellTypePattern() default ExcelColumnDataFormat.NONE;

    IndexedColors cellColor() default IndexedColors.AUTOMATIC;

    ExcelTotalFormula total() default ExcelTotalFormula.NONE;

}
ColumnExcel columnExcel = field.getAnnotation(ColumnExcel.class);
У міру розвитку програма отримала нову інструкцію, за допомогою якої в електронній таблиці можна було створити комірку з функцією всередині. Різні поля можна множити, віднімати, використовувати будь-які спільні функції Excel. Також я додав підсумковий рядок для відображення суми по стовпцю. І все це було я зробив лише за рахунок незначної модифікації основного парсера та простого додавання анотацій до класів.
@ColumnExcel(
            name = "Views",
            position = 4,
            total = ExcelTotalFormula.SUM)
    private BigDecimal variableC;

    @ColumnExcelFormula(
            name = "Conversion",
            position = 5,
            cellTypePattern = CellDataTypeFormatPattern.PERCENTAGE
    )
    public String variableD(int rowNumber) {
        return new CellAddress(rowNumber, 4).formatAsString() + "*"
		+ new CellAddress(rowNumber, 2).formatAsString();
    }

    @ColumnExcelTotalFormula(position = 4, cellTypePattern = CellDataTypeFormatPattern.RUR)
    public static String getVariableCTotalFormula(int firstRowNum, int lastRowNum) {
        return "SUM( " + new CellAddress(firstRowNum, 4).formatAsString() + ":"
		+ new CellAddress(lastRowNum, 4).formatAsString() + ")";
    }

Складання сміття в Java - як вона працює і в чому її переваги

Джерело: Dev.to Складання сміття означає знищення або очищення об'єктів, що не використовуються, в пам'яті. Java обробляє звільнення пам'яті автоматично, оскільки одного разу створений об'єкт використовує певний обсяг пам'яті в купі. Кава-брейк #88.  Сила метаданих: як працювати зі спагетті-кодом.  Складання сміття в Java - як вона працює і в чому її переваги - 2

Як це працює?

До Java найпопулярнішою мовою програмування був C чи C++. Якщо ви володієте цими мовами, то вам повинно бути відомо, що управління власною пам'яттю в них відбувається вручну. Наприклад, C є такі методи, як calloc() , malloc() і realloc() , які дозволять вам використовувати буферну пам'ять. Ви повинні визначити, скільки пам'яті вам потрібно для вашої програми, і вказати, що викликається цим API. Потім можна отримати буфер пам'яті для створення вузла зв'язаного списку або чогось іншого. При завершенні роботи вашої програми, в певний момент, ви також відповідаєте за очищення цієї пам'яті. Таким чином, велика програма, написана мовою C, продовжує виділяти буферну пам'ять і іноді забуває її очищати. Зрештою це викликає витоку пам'яті та безліч проблем у додатку. На відміну від C і C++, мова Java поставляється з автоматичним керуванням пам'яттю через потік, що називається збирачем сміття. Його основна мета – звільнити пам'ять купи шляхом знищення недоступних об'єктів. Складальник сміття завжди працює у фоновому режимі.

Що таке недоступні об'єкти Java?

Коли об'єкт отримує можливість розпочати складання сміття? За наявності недоступних об'єктів – тих, на які немає активних посилань. Давайте подивимося приклад:
public static void main(String[] args)
{
// StringBuffer object sb is not eligible for garbage collection
StringBuffer sb = new StringBuffer("Flower Brackets");
System.out.println(sb);
// StringBuffer object sb is eligible for garbage collection
sb = null;
}
В основному методі я створив об'єкт StringBuffer і посилання на нього. На даному етапі об'єкт StringBuffer не підходить для складання сміття. Тепер я збираюся привласнити об'єкту StringBuffer значення "null". Тепер об'єкт має право на складання сміття та стає недоступним об'єктом у пам'яті купи. Тобто складання сміття, як правило, працює в тих випадках, коли об'єкти стають недоступними. Це означає, що об'єкти зазвичай створюються у контексті “if block” або методу. Таким чином, об'єкти виходять за межі області видимості, як тільки виконання методу завершується, їх може видалити збирач сміття. Оскільки посилання зі старих об'єктів на нові існують в обмеженій кількості, це означає, що об'єкти, які тривалий час присутні у вашому додатку, зазвичай не належать до новостворених об'єктів. Ось пара термінів, з якими ми маємо бути знайомі; один із них — live-об'єкт. Це об'єкт у додатку, який посилається інший об'єкт у тому самому додатку. Також є “dead” (мертвий) об'єкт. Dead-об'єкт - це недоступний об'єкт, який створюється під час виклику методу, і як тільки виклик методу завершується, об'єкт виходить із контексту і просто лежить у купі.

Коли об'єкт має право на збирання сміття?

Якщо об'єкт не має будь-якої посилальної змінної, об'єкт має право на складання сміття.

Як зробити об'єкт доступним для складання сміття?

Нижче наведено кілька способів:
  1. null reference variable
    Student obj = new Student();
    obj = null;

  2. re-assign reference variable
    Student obj1 = new Student();
    Student obj2 = new Student();
    obj1 = obj2;

  3. reate anonymous object
    new Student();

    Після того, як об'єкт стає доступним збирачеві сміття, він не знищується відразу.

Коли віртуальна машина Java запускає збирач сміття, знищується лише об'єкт. ПРИМІТКА: збирач сміття збирає лише об'єкти, створені за допомогою ключового слова “new”, для об'єктів без ключового слова “new” використовуйте метод finalize() . Існує кілька методів для запуску збирача сміття у віртуальній машині Java:
  1. Метод System.gc()

  2. Метод finalize()

  3. Метод Runtime.getRuntime().gc()

Статичний метод gc() розташований у класі System . Цей метод запитує JVM виклик збирача сміття. Давайте подивимося, як Java-додаток за допомогою методу gc() викликає збирач сміття.
public class GarbageCollector
{
public static void main(String[] args)
{
Employee obj1 = new Employee();
Employee obj2 = new Employee();
obj1 = null;
obj2 = null;
System.gc();
}
public void finalize()
{
System.out.println("object garbage collected");
}
}
Результат:
object garbage collected object garbage collected
Метод finalize() викликається безпосередньо перед очищенням об'єкта. Цей метод визначений у класі Object :
protected void finalize() throws Throwable
  1. Метод Finalize використовується для закриття з'єднання з базою даних.

  2. Цей метод викликається збирачем сміття, а чи не JVM.

  3. Нам потрібно перевизначити метод finalize() . Тому що має порожню реалізацію.

  4. Він викликається лише один раз для кожного об'єкта.

Метод getRuntime().gc() є у класі часу виконання. Він повертає об'єкт Runtime , пов'язаний з поточною програмою Java. Давайте подивимося цей метод у Java-программе.
public class Demo
{
public static void main(String[] args)
{
Demo obj1 = new Demo();
Demo obj2 = new Demo();
// nullifying reference variable
obj1 = null;
// nullifying reference variable
obj2 = null;
// running Garbage Collector
Runtime.getRuntime().gc();
}
@Override
protected void finalize() throws Throwable
{
System.out.println("Garbage collector called");
System.out.println("Object garbage collector: " + this);
}
}
Результат:
Garbage collector called Object garbage collector: Demo@2130772 Garbage collector called Object garbage collector: Demo@cd4e940

Переваги складання сміття:

  1. Складання сміття в Java відбувається автоматично, що позбавляє нас від додаткового навантаження зі звільнення пам'яті, що використовується. Це робить пам'ять Java програми більш ефективною.
  2. Складання сміття забезпечує цілісність програми.
  3. Нам не потрібно писати додатковий код, оскільки збирач сміття є частиною JVM.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ