Перейти к содержимому

Getinstance java что это

  • автор:

Java. Шаблон Singleton

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

Синглтон (singleton) это класс, у которого экземпляр создаётся только один раз. Хорошим примером такого поведения служит файловая система, система работы с видео и т.д. В Android можно привести примеры с классами OkHttpClient, Retrofit, Gson, SharedPreferences. Для реализации синглтона нужно создать закрытый конструктор и открытый статический член, который и позволяет получить доступ к единственному экземпляру класса. Например, так.

 public class Single < public static final Single INSTANCE = new Single(); private Single()<>public void someMethod() < Log.i("Log", "I am someMethod"); >> 

Закрытый конструктор вызывается один раз для инициализации поля INSTANCE. Открытых конструктор у класса нет, поэтому после инициализации класса будет существовать только один экземпляр Single.

Существует другой вариант, когда вместо открытого статического поля создаётся открытый статический метод, а поле становится закрытым.

 public class Single < private static final Single INSTANCE = new Single(); private Single()<>public void someMethod() < Log.i("Log", "I am a someMethod"); >public static Single getInstance() < return INSTANCE; >> 

Второй способ удобен тем, что вы можете позже отказаться от синглтона, но вам не придётся сильно переписывать код.

Позже был придуман третий вариант с использованием enum.

Одиночка

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

Паттерн Одиночка

Проблема

Одиночка решает сразу две проблемы, нарушая принцип единственной ответственности класса.

Глобальный доступ к одному объекту

  1. Гарантирует наличие единственного экземпляра класса. Чаще всего это полезно для доступа к какому-то общему ресурсу, например, базе данных. Представьте, что вы создали объект, а через некоторое время пробуете создать ещё один. В этом случае хотелось бы получить старый объект, вместо создания нового. Такое поведение невозможно реализовать с помощью обычного конструктора, так как конструктор класса всегда возвращает новый объект.
  1. Предоставляет глобальную точку доступа. Это не просто глобальная переменная, через которую можно достучаться к определённому объекту. Глобальные переменные не защищены от записи, поэтому любой код может подменять их значения без вашего ведома. Но есть и другой нюанс. Неплохо бы хранить в одном месте и код, который решает проблему №1, а также иметь к нему простой и доступный интерфейс.

Интересно, что в наше время паттерн стал настолько известен, что теперь люди называют «одиночками» даже те классы, которые решают лишь одну из проблем, перечисленных выше.

Решение

Все реализации одиночки сводятся к тому, чтобы скрыть конструктор по умолчанию и создать публичный статический метод, который и будет контролировать жизненный цикл объекта-одиночки.

Если у вас есть доступ к классу одиночки, значит, будет доступ и к этому статическому методу. Из какой точки кода вы бы его ни вызвали, он всегда будет отдавать один и тот же объект.

Аналогия из жизни

Правительство государства — хороший пример одиночки. В государстве может быть только одно официальное правительство. Вне зависимости от того, кто конкретно заседает в правительстве, оно имеет глобальную точку доступа «Правительство страны N».

Структура

Структура классов паттерна Одиночка

  1. Одиночка определяет статический метод getInstance , который возвращает единственный экземпляр своего класса. Конструктор одиночки должен быть скрыт от клиентов. Вызов метода getInstance должен стать единственным способом получить объект этого класса.

Псевдокод

В этом примере роль Одиночки отыгрывает класс подключения к базе данных.

Этот класс не имеет публичного конструктора, поэтому единственный способ получить его объект — это вызвать метод getInstance . Этот метод сохранит первый созданный объект и будет возвращать его при всех последующих вызовах.

// Класс одиночки определяет статический метод `getInstance`, // который позволяет клиентам повторно использовать одно и то же // подключение к базе данных по всей программе. class Database is // Поле для хранения объекта-одиночки должно быть объявлено // статичным. private static field instance: Database // Конструктор одиночки всегда должен оставаться приватным, // чтобы клиенты не могли самостоятельно создавать // экземпляры этого класса через оператор `new`. private constructor Database() is // Здесь может жить код инициализации подключения к // серверу баз данных. // . // Основной статический метод одиночки служит альтернативой // конструктору и является точкой доступа к экземпляру этого // класса. public static method getInstance() is if (Database.instance == null) then acquireThreadLock() and then // На всякий случай ещё раз проверим, не был ли // объект создан другим потоком, пока текущий // ждал освобождения блокировки. if (Database.instance == null) then Database.instance = new Database() return Database.instance // Наконец, любой класс одиночки должен иметь какую-то // полезную функциональность, которую клиенты будут // запускать через полученный объект одиночки. public method query(sql) is // Все запросы к базе данных будут проходить через этот // метод. Поэтому имеет смысл поместить сюда какую-то // логику кеширования. // . class Application is method main() is Database foo = Database.getInstance() foo.query("SELECT . ") // . Database bar = Database.getInstance() bar.query("SELECT . ") // Переменная "bar" содержит тот же объект, что и // переменная "foo".

Применимость

Когда в программе должен быть единственный экземпляр какого-то класса, доступный всем клиентам (например, общий доступ к базе данных из разных частей программы).

Одиночка скрывает от клиентов все способы создания нового объекта, кроме специального метода. Этот метод либо создаёт объект, либо отдаёт существующий объект, если он уже был создан.

Когда вам хочется иметь больше контроля над глобальными переменными.

В отличие от глобальных переменных, Одиночка гарантирует, что никакой другой код не заменит созданный экземпляр класса, поэтому вы всегда уверены в наличии лишь одного объекта-одиночки.

Тем не менее, в любой момент вы можете расширить это ограничение и позволить любое количество объектов-одиночек, поменяв код в одном месте (метод getInstance ).

Шаги реализации

  1. Добавьте в класс приватное статическое поле, которое будет содержать одиночный объект.
  2. Объявите статический создающий метод, который будет использоваться для получения одиночки.
  3. Добавьте «ленивую инициализацию» (создание объекта при первом вызове метода) в создающий метод одиночки.
  4. Сделайте конструктор класса приватным.
  5. В клиентском коде замените вызовы конструктора одиночка вызовами его создающего метода.

Преимущества и недостатки

  • Гарантирует наличие единственного экземпляра класса.
  • Предоставляет к нему глобальную точку доступа.
  • Реализует отложенную инициализацию объекта-одиночки.
  • Нарушает принцип единственной ответственности класса.
  • Маскирует плохой дизайн.
  • Проблемы мультипоточности.
  • Требует постоянного создания Mock-объектов при юнит-тестировании.

Отношения с другими паттернами

  • Фасад можно сделать Одиночкой, так как обычно нужен только один объект-фасад.
  • Паттерн Легковес может напоминать Одиночку, если для конкретной задачи у вас получилось свести количество объектов к одному. Но помните, что между паттернами есть два кардинальных отличия:
    1. В отличие от Одиночки, вы можете иметь множество объектов-легковесов.
    2. Объекты-легковесы должны быть неизменяемыми, тогда как объект-одиночка допускает изменение своего состояния.
  • Абстрактная фабрика, Строитель и Прототип могут быть реализованы при помощи Одиночки.

Примеры реализации паттерна

Не втыкай в транспорте

Лучше почитай нашу книгу о паттернах проектирования.

Теперь это удобно делать даже во время поездок в общественном транспорте.

Эта статья является частью нашей электронной книги Погружение в Паттерны Проектирования.

Refactoring.Guru

  • Премиум контент
    • Книга о паттернах
    • Курс по рефакторингу
  • Рефакторинг
    • Введение в рефакторинг
      • Чистый код
      • Технический долг
      • Когда рефакторить
      • Как рефакторить
    • Каталог
    • Запахи кода
      • Раздувальщики
        • Длинный метод
        • Большой класс
        • Одержимость элементарными типами
        • Длинный список параметров
        • Группы данных
      • Нарушители объектно-ориентированного дизайна
        • Операторы switch
        • Временное поле
        • Отказ от наследства
        • Альтернативные классы с разными интерфейсами
      • Утяжелители изменений
        • Расходящиеся модификации
        • Стрельба дробью
        • Параллельные иерархии наследования
      • Замусориватели
        • Комментарии
        • Дублирование кода
        • Ленивый класс
        • Класс данных
        • Мёртвый код
        • Теоретическая общность
      • Опутыватели связями
        • Завистливые функции
        • Неуместная близость
        • Цепочка вызовов
        • Посредник
      • Остальные запахи
        • Неполнота библиотечного класса
    • Техники
      • Составление методов
        • Извлечение метода
        • Встраивание метода
        • Извлечение переменной
        • Встраивание переменной
        • Замена переменной вызовом метода
        • Расщепление переменной
        • Удаление присваиваний параметрам
        • Замена метода объектом методов
        • Замена алгоритма
      • Перемещение функций между объектами
        • Перемещение метода
        • Перемещение поля
        • Извлечение класса
        • Встраивание класса
        • Сокрытие делегирования
        • Удаление посредника
        • Введение внешнего метода
        • Введение локального расширения
      • Организация данных
        • Самоинкапсуляция поля
        • Замена простого поля объектом
        • Замена значения ссылкой
        • Замена ссылки значением
        • Замена поля-массива объектом
        • Дублирование видимых данных
        • Замена однонаправленной связи двунаправленной
        • Замена двунаправленной связи однонаправленной
        • Замена магического числа символьной константой
        • Инкапсуляция поля
        • Инкапсуляция коллекции
        • Замена кодирования типа классом
        • Замена кодирования типа подклассами
        • Замена кодирования типа состоянием/стратегией
        • Замена подкласса полями
      • Упрощение условных выражений
        • Разбиение условного оператора
        • Объединение условных операторов
        • Объединение дублирующихся фрагментов в условных операторах
        • Удаление управляющего флага
        • Замена вложенных условных операторов граничным оператором
        • Замена условного оператора полиморфизмом
        • Введение Null-объекта
        • Введение проверки утверждения
      • Упрощение вызовов методов
        • Переименование метода
        • Добавление параметра
        • Удаление параметра
        • Разделение запроса и модификатора
        • Параметризация метода
        • Замена параметра набором специализированных методов
        • Передача всего объекта
        • Замена параметра вызовом метода
        • Замена параметров объектом
        • Удаление сеттера
        • Сокрытие метода
        • Замена конструктора фабричным методом
        • Замена кода ошибки исключением
        • Замена исключения проверкой условия
      • Решение задач обобщения
        • Подъём поля
        • Подъём метода
        • Подъём тела конструктора
        • Спуск метода
        • Спуск поля
        • Извлечение подкласса
        • Извлечение суперкласса
        • Извлечение интерфейса
        • Свёртывание иерархии
        • Создание шаблонного метода
        • Замена наследования делегированием
        • Замена делегирования наследованием
  • Паттерны
    • Введение в паттерны
      • Что такое Паттерн?
      • История паттернов
      • Зачем знать паттерны?
      • Критика паттернов
      • Классификация паттернов
    • Каталог
    • Порождающие
      • Фабричный метод
      • Абстрактная фабрика
      • Строитель
      • Прототип
      • Одиночка
    • Структурные
      • Адаптер
      • Мост
      • Компоновщик
      • Декоратор
      • Фасад
      • Легковес
      • Заместитель
    • Поведенческие
      • Цепочка обязанностей
      • Команда
      • Итератор
      • Посредник
      • Снимок
      • Наблюдатель
      • Состояние
      • Стратегия
      • Шаблонный метод
      • Посетитель
    • Примеры кода
      • C#
      • C++
      • Go
      • Java
      • PHP
      • Python
      • Ruby
      • Rust
      • Swift
      • TypeScript

Одиночка на Java

Одиночка

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

Одиночка имеет такие же преимущества и недостатки, что и глобальные переменные. Его невероятно удобно использовать, но он нарушает модульность вашего кода.

Вы не сможете просто взять и использовать класс, зависящий от одиночки в другой программе. Для этого придётся эмулировать присутствие одиночки и там. Чаще всего эта проблема проявляется при написании юнит-тестов.

Сложность:

Популярность:

Применимость: Многие программисты считают Одиночку антипаттерном, поэтому его всё реже и реже можно встретить в Java-коде.

Тем не менее, Одиночке нашлось применение в стандартных библиотеках Java:

  • java.lang.Runtime#getRuntime()
  • java.awt.Desktop#getDesktop()
  • java.lang.System#getSecurityManager()

Признаки применения паттерна: Одиночку можно определить по статическому создающему методу, который возвращает один и тот же объект.

Наивный Одиночка (один поток)

Топорно реализовать Одиночку очень просто — достаточно скрыть конструктор и предоставить статический создающий метод.

Singleton.java: Одиночка
package refactoring_guru.singleton.example.non_thread_safe; public final class Singleton < private static Singleton instance; public String value; private Singleton(String value) < // Этот код эмулирует медленную инициализацию. try < Thread.sleep(1000); >catch (InterruptedException ex) < ex.printStackTrace(); >this.value = value; > public static Singleton getInstance(String value) < if (instance == null) < instance = new Singleton(value); >return instance; > >
DemoSingleThread.java: Клиентский код
package refactoring_guru.singleton.example.non_thread_safe; public class DemoSingleThread < public static void main(String[] args) < System.out.println("If you see the same value, then singleton was reused (yay!)" + "\n" + "If you see different values, then 2 singletons were created (booo!!)" + "\n\n" + "RESULT:" + "\n"); Singleton singleton = Singleton.getInstance("FOO"); Singleton anotherSingleton = Singleton.getInstance("BAR"); System.out.println(singleton.value); System.out.println(anotherSingleton.value); >>
OutputDemoSingleThread.txt: Результаты выполнения
If you see the same value, then singleton was reused (yay!) If you see different values, then 2 singletons were created (booo!!) RESULT: FOO FOO

Наивный Одиночка (много потоков)

Тот же класс ведёт себя неправильно в многопоточной среде. Несколько потоков могут одновременно вызвать метод получения Одиночки и создать сразу несколько экземпляров объекта.

Singleton.java: Одиночка
package refactoring_guru.singleton.example.non_thread_safe; public final class Singleton < private static Singleton instance; public String value; private Singleton(String value) < // Этот код эмулирует медленную инициализацию. try < Thread.sleep(1000); >catch (InterruptedException ex) < ex.printStackTrace(); >this.value = value; > public static Singleton getInstance(String value) < if (instance == null) < instance = new Singleton(value); >return instance; > >
DemoMultiThread.java: Клиентский код
package refactoring_guru.singleton.example.non_thread_safe; public class DemoMultiThread < public static void main(String[] args) < System.out.println("If you see the same value, then singleton was reused (yay!)" + "\n" + "If you see different values, then 2 singletons were created (booo!!)" + "\n\n" + "RESULT:" + "\n"); Thread threadFoo = new Thread(new ThreadFoo()); Thread threadBar = new Thread(new ThreadBar()); threadFoo.start(); threadBar.start(); >static class ThreadFoo implements Runnable < @Override public void run() < Singleton singleton = Singleton.getInstance("FOO"); System.out.println(singleton.value); >> static class ThreadBar implements Runnable < @Override public void run() < Singleton singleton = Singleton.getInstance("BAR"); System.out.println(singleton.value); >> >
OutputDemoMultiThread.txt: Результаты выполнения
If you see the same value, then singleton was reused (yay!) If you see different values, then 2 singletons were created (booo!!) RESULT: FOO BAR

Многопоточный Одиночка

Чтобы исправить проблему, требуется синхронизировать потоки при создании объекта-Одиночки.

Singleton.java: Одиночка
package refactoring_guru.singleton.example.thread_safe; public final class Singleton < // Поле обязательно должно быть объявлено volatile, чтобы двойная проверка // блокировки сработала как надо. private static volatile Singleton instance; public String value; private Singleton(String value) < this.value = value; >public static Singleton getInstance(String value) < // Техника, которую мы здесь применяем называется «блокировка с двойной // проверкой» (Double-Checked Locking). Она применяется, чтобы // предотвратить создание нескольких объектов-одиночек, если метод будет // вызван из нескольких потоков одновременно. // // Хотя переменная `result` вполне оправданно кажется здесь лишней, она // помогает избежать подводных камней реализации DCL в Java. // // Больше об этой проблеме можно почитать здесь: // https://refactoring.guru/ru/java-dcl-issue Singleton result = instance; if (result != null) < return result; >synchronized(Singleton.class) < if (instance == null) < instance = new Singleton(value); >return instance; > > >
DemoMultiThread.java: Клиентский код
package refactoring_guru.singleton.example.thread_safe; public class DemoMultiThread < public static void main(String[] args) < System.out.println("If you see the same value, then singleton was reused (yay!)" + "\n" + "If you see different values, then 2 singletons were created (booo!!)" + "\n\n" + "RESULT:" + "\n"); Thread threadFoo = new Thread(new ThreadFoo()); Thread threadBar = new Thread(new ThreadBar()); threadFoo.start(); threadBar.start(); >static class ThreadFoo implements Runnable < @Override public void run() < Singleton singleton = Singleton.getInstance("FOO"); System.out.println(singleton.value); >> static class ThreadBar implements Runnable < @Override public void run() < Singleton singleton = Singleton.getInstance("BAR"); System.out.println(singleton.value); >> >
OutputDemoMultiThread.txt: Результаты выполнения
If you see the same value, then singleton was reused (yay!) If you see different values, then 2 singletons were created (booo!!) RESULT: BAR BAR

Хотите ещё?

Существует ещё с полдюжины способов реализации Одиночки в Java. Если интересно, можете ознакомиться с ними здесь:

Одиночка на других языках программирования

Купи книгу Погружение в Паттерны и получи архив с десятками детальных примеров, которые можно открывать прямо в IDE.

Refactoring.Guru

  • Премиум контент
    • Книга о паттернах
    • Курс по рефакторингу
  • Рефакторинг
    • Введение в рефакторинг
      • Чистый код
      • Технический долг
      • Когда рефакторить
      • Как рефакторить
    • Каталог
    • Запахи кода
      • Раздувальщики
        • Длинный метод
        • Большой класс
        • Одержимость элементарными типами
        • Длинный список параметров
        • Группы данных
      • Нарушители объектно-ориентированного дизайна
        • Операторы switch
        • Временное поле
        • Отказ от наследства
        • Альтернативные классы с разными интерфейсами
      • Утяжелители изменений
        • Расходящиеся модификации
        • Стрельба дробью
        • Параллельные иерархии наследования
      • Замусориватели
        • Комментарии
        • Дублирование кода
        • Ленивый класс
        • Класс данных
        • Мёртвый код
        • Теоретическая общность
      • Опутыватели связями
        • Завистливые функции
        • Неуместная близость
        • Цепочка вызовов
        • Посредник
      • Остальные запахи
        • Неполнота библиотечного класса
    • Техники
      • Составление методов
        • Извлечение метода
        • Встраивание метода
        • Извлечение переменной
        • Встраивание переменной
        • Замена переменной вызовом метода
        • Расщепление переменной
        • Удаление присваиваний параметрам
        • Замена метода объектом методов
        • Замена алгоритма
      • Перемещение функций между объектами
        • Перемещение метода
        • Перемещение поля
        • Извлечение класса
        • Встраивание класса
        • Сокрытие делегирования
        • Удаление посредника
        • Введение внешнего метода
        • Введение локального расширения
      • Организация данных
        • Самоинкапсуляция поля
        • Замена простого поля объектом
        • Замена значения ссылкой
        • Замена ссылки значением
        • Замена поля-массива объектом
        • Дублирование видимых данных
        • Замена однонаправленной связи двунаправленной
        • Замена двунаправленной связи однонаправленной
        • Замена магического числа символьной константой
        • Инкапсуляция поля
        • Инкапсуляция коллекции
        • Замена кодирования типа классом
        • Замена кодирования типа подклассами
        • Замена кодирования типа состоянием/стратегией
        • Замена подкласса полями
      • Упрощение условных выражений
        • Разбиение условного оператора
        • Объединение условных операторов
        • Объединение дублирующихся фрагментов в условных операторах
        • Удаление управляющего флага
        • Замена вложенных условных операторов граничным оператором
        • Замена условного оператора полиморфизмом
        • Введение Null-объекта
        • Введение проверки утверждения
      • Упрощение вызовов методов
        • Переименование метода
        • Добавление параметра
        • Удаление параметра
        • Разделение запроса и модификатора
        • Параметризация метода
        • Замена параметра набором специализированных методов
        • Передача всего объекта
        • Замена параметра вызовом метода
        • Замена параметров объектом
        • Удаление сеттера
        • Сокрытие метода
        • Замена конструктора фабричным методом
        • Замена кода ошибки исключением
        • Замена исключения проверкой условия
      • Решение задач обобщения
        • Подъём поля
        • Подъём метода
        • Подъём тела конструктора
        • Спуск метода
        • Спуск поля
        • Извлечение подкласса
        • Извлечение суперкласса
        • Извлечение интерфейса
        • Свёртывание иерархии
        • Создание шаблонного метода
        • Замена наследования делегированием
        • Замена делегирования наследованием
  • Паттерны
    • Введение в паттерны
      • Что такое Паттерн?
      • История паттернов
      • Зачем знать паттерны?
      • Критика паттернов
      • Классификация паттернов
    • Каталог
    • Порождающие
      • Фабричный метод
      • Абстрактная фабрика
      • Строитель
      • Прототип
      • Одиночка
    • Структурные
      • Адаптер
      • Мост
      • Компоновщик
      • Декоратор
      • Фасад
      • Легковес
      • Заместитель
    • Поведенческие
      • Цепочка обязанностей
      • Команда
      • Итератор
      • Посредник
      • Снимок
      • Наблюдатель
      • Состояние
      • Стратегия
      • Шаблонный метод
      • Посетитель
    • Примеры кода
      • C#
      • C++
      • Go
      • Java
      • PHP
      • Python
      • Ruby
      • Rust
      • Swift
      • TypeScript

Getinstance java что это

«Отмечу, что для корректной работы данного варианта реализации обязательно одно из двух условий. Переменная INSTANCE должна быть либо final, либо volatile. » При этом, в примере кода, переменная не final и неvolatile))

dvazhdydva Уровень 22
2 ноября 2022

в статье на хабре написано: 1) Использовать нормальную (не ленивую) инициализацию везде где это возможно;, а здесь: Когда использовать? Никогда. Либо когда не важна ленивая инициализация. Но лучше никогда. кто прав?

Roman Eroshenko Уровень 24
23 сентября 2022

Высокая производительность в многопоточной среде: потоки блокируют друг друга минимально, либо вообще не блокируют при совместном доступе к ресурсу. Теперь минусы. Перечислим критерии, которые выставляют реализацию в нелучшем свете: Низкая производительность в многопоточной среде: потоки блокируют друг друга все время либо часто, при совместном доступе к ресурсу. Не понял прикола

Rostik Уровень 36
20 апреля 2022

Касаемо раздела статьи «Double Checked Locking». Автор забыл обязательное в данном случае ключевое слово volatile. Смотрим, почему. Да и в принципе, guys, советую целиком чекнуть данное видео. Довольно чётко & ясно про азы multithreading.��

CyberBoar Уровень 39
12 апреля 2022
И как в последнем случае нам гарантировать, что объект инициализируется без ошибок?
Denis Уровень 33
22 декабря 2021

Я запутался. Эта статья и другие источники рассказывают нам о том, что в JVM реализована «ленивая загрузка классов», т.е. загрузка классов происходит не сразу, а при первом обращении к ним. Так же я помню из других источников, что инициализация статических полей и статических блоков класса выполняется ОДИН РАЗ ПРИ ПЕРВОМ ОБРАЩЕНИИ К КЛАССУ. Получается, что если разработчик в своём коде не обращается к объекту класса Singletone, то и статические поля и статические блоки выполнятся не будут, а значит и не будет создан экземпляр класса. Тогда единственный минус первого примера как бы отпадает — верно? А значит первый вариант — самый простой и правильный вариант паттерна Singletone. Кто может объяснить мне, почему в статье в самом первом примере указан минус паттерна «Не ленивая инициализация»?

Игорь Уровень 41
9 августа 2021

Никого не смущает последний — Class Holder Singleton, якобы лучший пример? Зачем понадобился вложенный класс, если получается тоже самое, что и в самом первом примере.

Владимир Лукашов Уровень 40
19 июля 2021

 if (INSTANCE == null) INSTANCE = new SingletonExample1(); return INSTANCE; 

Так Singleton работает правильно а если написать вот так

 return (INSTANCE == null) ? new SingletonExample1() : INSTANCE; 

то создается новый объект каждый раз SingletonDemo < example1=Singleton.SingletonExample1@67b64c45 >SingletonDemo < example1=Singleton.SingletonExample1@4411d970 >SingletonDemo < example1=Singleton.SingletonExample1@6442b0a6 >SingletonDemo < example1=Singleton.SingletonExample1@60f82f98 >По факту суть кода одна но результат разный. Есть люди кто разбирался в этом? Я че то догнать не могу

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *