Как создать метод в java: конструктор, ключевые слова this и void, finalize() / ProgLang


19.05.1981 Facebook Twitter LinkedIn Google+ Разное


Содержание

Методы класса Object в Java

Пришло время поговорить о том, откуда появляются методы “по-умолчанию” у ваших классов. Возможно, вы замечали уже, что у ваших классов помимо методов, написанных вами, есть и другие. К примеру: toString(), equals(), hashCode() и другие.

Так вот, секрет состоит в том, что эти методы принадлежат классу Object, а все создающиеся классы по-умолчанию наследуются от класса Object (т.е. не надо сейчас идти и всем своим классам добавлять “extends Object”. Java делает это за вас. За вашей спиной. Остерегайтесь, вы, возможно, тоже унаследованы от объекта, а мир — это матрица).

  Посмотрим же на методы, которые мы получаем по-умолчанию для своего класса:

wait(), notify(), notifyAll() — три метода из набора для многопоточности. Не уверен, что в этом курсе расскажу про это, так как —  это довольно обширная и сложная тема.

Однако, даже по названиям можно понять, что эти методы способны заставить некоторых потом ждать (wait) и, наоборот — пробудить (notify).

getClass() — получить класс объекта во время выполнения. В основном используется для рефлексии.

clone() — получить точную копию объекта. Не рекомендуется использовать. Чаще советуют использовать конструктор копирования.

equals() — сравнивает два объекта.

hashcode() — числовое представление объекта. Значение по-умолчанию — целочисленный адрес в памяти.

toString() — возвращает представление объекта в виде строки. По-умолчанию возвращает имя_класса@hashcode в 16-ричной системе. Если hashcode не переопределен, то вернут значение по-умолчанию.

Иногда для коллекций (в следующей статье) требуется переопределить equals и hashcode.

Правила, которые следует учесть при переопределении equals:

  • Рефлексивность: x. equals(x) == true
  • Симметричность: если x.equals(y), тогда y.equals(x) == true
  • Переносимость: если x.equals(y) и y.equals(z), тогда x.equals(z) == true
  • Консистентность: если x.equals(y), тогда x.equals(y) == true
  • Null проверка: x.equals(null) == false

Отвечаю на вопрос — почему нужно переопределить hashcode? Дело в том, что значение по-умолчанию, как писалось выше, это адрес в памяти. То есть может случиться такая ситуация, что вы переопределили equals и объекты равным по этому equals, но они находятся на разных адресах памяти и это нарушает соглашение между equals и hashcode:

    Если объекты равны по equals — они должны быть равны и по hashcode. Если объекты не равны по equals, то их hashcode может быть равен, а может и нет

.

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

Собственно по теме методов Object — это все. Для практики можно создать некий класс и, так сказать, пощупать методы поближе. Если не выйдет переопределить equals и hashcode, то не переживайте. В теме по Java коллекциям будет пример.

Весь цикл читайте на страничке Сергея Посьмашного 

 

Похожие темы

Классы. Часть 1 – Введение.

Как уже не раз говорилось в Java все является объектом, за исключением примитивных типов.

Объекты создаются из классов. Поэтому класс является центральным компонентом в Java. Поскольку класс определяет форму и сущность объекта, он является той логической конструкцией, на основе которой построен весь язык. Как таковой, класс образует основу объектно-ориентированного программирования в среде Java. Любая концепция, которую нужно реализовать в Java-программе, должна быть помещена внутрь класса. В связи с тем, что класс имеет такое большое значение для Java мы рассмотрим его очень подробно.

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

Еще раз напомню, что класс — это шаблон объекта, а объект — это экземпляр класса. Поскольку объект является экземпляром класса, термины объект и экземпляр часто используются попеременно.

Объявление класса

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

Чтобы все было более понятно, приведу процесс объявления класса в нескольких простых слайдах…

Для объявления класса служит ключевое слово class.

Тут представлена самая простоя форма создания класса. В нем пока отсутствуют данные (поля класса – fields) и код (методы класса – methods). Тело класса, то есть поля и методы должны быть заключены между фигурными скобками.

Имя файла, в котором храниться текст класса, должно в точности совпадать с именем класса учитывая регистр букв. То есть, в нашем примере класс Example должен находится в файле Example.java.

Модификаторы (modifiers) могут отсутствовать при объявлении класса. О модификаторах мы поговорим чуть позже.

Как уже говорилось, класс может содержать данные (поля – fields

) и код (методы – methods).

Рассмотрим добавление полей в класс.

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

Полей в классе может быть сколько угодно.

Ну и сразу же рассмотрим пример создания простого класса, где будут только поля данных но не будет методов. Чтобы это продемонстрировать нам понадобятся два класса, один с методом main() и другой который мы создадим сами, например класс Box.

Вот мы и создали наш первый класс Box, весь такой из себя маленький и красивый. Как видим все его поля находятся между фигурными скобками, а имя файла в котором он хранится – Box.java.

Как мы помним для создания объектов используется оператор

new, который выделяет в heap’e (куче) память под объект, где так же размещаются все поля (данные) класса. При выделении памяти происходит инициализация полей.

Если нет конструкторов в классе (о которых мы поговорим чуть позже) которые инициализируют эти поля какими-то значениями, то поля инициализируются значениями по умолчанию. Это делает конструктор по умолчанию.

Еще раз акцентирую внимание что поля класса создаются в куче (heap’e), поэтому, даже если вы их не проинициализировали какими-либо значениями, их сразу же можно использовать в коде программы, так как дефолтный конструктор присваивает им значения по умолчанию при создании объекта. Переменные же в методах класса создаются в стеке, поэтому их необходимо инициализировать значениями перед использованием, так как там могут быть неизвестно какие значения.

Как уже было сказано, класс определяет новый тип данных. В данном случае новый тип данных назван Box. Это имя будет использоваться для объявления объектов типа Box. Важно понимать, что объявление class создает только шаблон, но не действительный объект. Таким образом, приведенный код не приводит к появлению никаких объектов типа Box.

Как я уже упоминал, чтобы создать объект типа Box нужно использовать оператор new. В данном примере слева, в строке 7 мы как раз и создаем новый объект Box и размещаем ссылку на него в переменную box1. Далее мы можем использовать этот объект и обратиться к его полям через ссылку на него – box1.

Для обращения к полям объекта используется точка (.) после имени объекта (ссылки на объект), например – box1. widht.

Вывод этой программы вы видите слева. Все поля получили значения по умолчанию.

 

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

println

(«box1.label = » + box1.label);

Вывод измененного класса Classes001 представлен справа.

 

К полям экземпляра, можно не только обращаться для чтения их значений, но и для изменения, хотя обычно так делать не рекомендуют. Поскольку обычно поля лучше менять через методы класса и именно так и рекомендуется делать – это и есть инкапсуляция, ну в самом простом ее понимании. На примере слева, в строке 14 мы изменили значение поля label для объекта box1.

Вывод у программы теперь следующий:

Как вы уже должны были понять, наша программа состоит из двух классов: Classes001 и Box.

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

Что такое пакеты мы рассмотрим чуть позже, а пока просто о них упомянули.

Каждый объект содержит собственные копии полей экземпляра. Это означает, что при наличии двух объектов Box каждый из них будет содержать собственные копии полей depth, width, height и label. Важно понимать, что изменения переменных экземпляра одного объекта не влияют на переменные экземпляра другого.

На примере слева, в строке 17 мы создали еще одни экземпляр класса Box с именем box2 и в строке 18 присвоили полю label значение b0x.

Вывод у программы теперь такой:

Поля box2.label и box1.label имеют разные значения.

Чтобы лучше понять чем отличаются классы от объектов и как экземпляры классов (объекты) создаются в памяти, очень рекомендую посмотреть следующее видео:

Создание экземпляра класса (объекта)

Теперь зададим значения высоты, ширины и глубины для box2 и box1 и посчитаем объем каждого из ящиков. Данный пример я привожу для того, чтобы в следующем разделе, где будем рассматривать объявление методов, сразу же стала понята их полезность и назначение.

Теперь вывод у программы следующий:

Как видно из текста класса Classes001, в строка 25 и 30, для вычисления объема ящика, нам каждый раз необходимо обращаться ко всем трем полям экземпляра класса, что не очень то удобно, если таких ящиков будут тысячи.

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

Так же для вывода значений высоты, ширины и глубины, нам опять каждый раз надо обращаться к каждому из полей экземпляра класса Box. А что если создать метод, который будет выводить их все сразу? Ведь это куда удобней!

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

И так! Погнали дальше!

Объявление методов в классе, обычно делают после объявления переменных. Например, объявление метода может быть таким:

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

Кроме того, метод может возвращать какое-либо значение, как в нашем примере возвращает значение типа int через оператор return, а может не возвращать, и тогда тип возвращаемого значения указывается как void.

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

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

Здесь тип указывает тип данных, возвращаемых методом. Он может быть любым допустимым типом, в том числе типом класса, созданным программистом. Если метод не возвращает значение, типом его возвращаемого значения должен быть void. Имя служит для указания имени метода. Оно может быть любым допустимым идентификатором, кроме тех, которые уже используются другими элементами в текущей области определения. Список_параметров — последовательность пар “тип-идентификатор”, разделенных запятыми. По сути, параметры — это переменные, которые принимают значения аргументов, переданных методу во время его вызова. Если метод не имеет параметров, список параметров будет пустым.

Методы, тип возвращаемого значения которых отличается от void, возвращают значение вызывающей процедуре с помощью следующей формы оператора return:

Здесь значение — это возвращаемое значение. Но оператор return может использоваться и без возвращаемого значения если тип метода void, в этом случае оператор return просто прерывает выполнение метода.

Ну и теперь попрактикуемся. Создадим методы в классе Box, что на примере слева.

Метод printVolume() не возвращает ни какого значения, поэтому возвращаемый тип указан как void. Данный метод просто выводит на консоль значение объема.

Метод printSizes() тоже не возвращает ни какого значения, а просто выводит на консоль значения полей.

Метод getVolume() возвращает значение типа double, которое вычисляется перемножением полей размеров и возвращается оператором return.

Метод setSameSize(double size), хотя не возвращает ни какого значения, но напротив устанавливает значения полей равными переданному аргументу в переменной size, имеющей тип double.

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

Обращение к методам объекта делается так же, как и обращение к полям объекта, то есть через точку (. ), что мы и увидим в классе Classes001.

Стоит взять на заметку, что мы не использовали ни каких модификаторов, ни для полей, ни для класса, ни для методов.

 

Теперь в классе Classes001 мы можем внести изменения, показанные на примере слева.

В строках 10-12 мы задали размеры для box1 так же как и прежде.

Но в строке 14 для объекта box2 мы уже использовали метод setSameSize() с параметром равным 15, который мы передали в метод.

Метод setSameSize() принял этот параметр и установил значения всех полей размеров равным этому значению (см. код класса Box выше).

Затем в строках 17 и 23 мы использовали метод printSizes() для каждого из объектов, который вывел на консоль их размеры.

В строках 18 и 24 мы использовали метод getVolume() для получения значения объема объектов.

На заметку. С этого момента, то есть с момента начала данного урока, я буду многократно изменять тексты классов. На Bitbucket в исходниках вы будете видеть только последнюю версию изменений. И кстати там же можете посмотреть и всю историю изменений каждого класса.

Вывод у нашей программы теперь такой:

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

Обратите внимание, что в метод передаются три параметра.

Вызов этого метода для объекта box1 осуществляется следующим образом:

box1

.setSizes(10, 20, 30);

Вывод программы не изменился, зато код класса Calsses001 сократился на две строки, так как теперь все три значения мы задаем в одной строке с помощью вызова метода setSizes(). На Bitbucket можно посмотреть как теперь выглядят классы Classess001 и Box.

Еще раз прошу обратить внимание на то каким образом метод определен, как заданы параметры передающиеся в него и как называются  эти переменные (w, h и d). Это нам скоро понадобится.

А пока переходим к конструкторам класса. Конструкторы, это по существу, те же самые методы, но которые автоматически вызываются при создании объекта, если они были объявлены в классе и используются при создании объекта.

Конструктор инициализирует объект непосредственно во время создания. Его имя совпадает с именем класса, в котором он находится, а синтаксис аналогичен синтаксису метода. Как только он определен, конструктор автоматически вызывается непосредственно после создания объекта, перед завершением выполнения операции new.

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

Если не объявлен ни один конструктор, автоматически создается конструктор по умолчанию (без параметров).

Объявление конструктора может выглядеть так:

Модификаторы, как и в предыдущих случаях, могут отсутствовать. И как видите имя конструктора совпадает с именем класса. Так же в примере использовано ключевое слово this, но мы рассмотрим его чуть позже, так как это отдельная тема для большого разговора. А пока рассмотрим более простой пример объявления конструктора.

Добавим в наш класс Box, конструктор, который будет инициализировать все размеры.

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

Не находите, что он очень похож на метод setSizes()?

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

Инициализация происходит путем присвоения значений переданных аргументов полям класса.

 

Теперь выполнить создание и инициализацию объекта типа Box можно следующим образом:

Box

box1 = new Box(10, 20, 30);
Box box2 = new Box(15, 15, 15);

Вывод данной программы не поменялся, но зато класс Classes001 сократился еще на несколько строк и теперь выглядит так:

Теперь размеры для объектов задаются прямо при их создании.

Чтобы было лучшее понимание происходящего, то лучше посмотреть текущее состояние кода классов Classes001 и Box.

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

На данный момент пока все достаточно просто и понятно, но все же, в такой реализации конструктора есть подвох, так как мы теперь не можем использовать конструктор по умолчанию, то есть не можем использовать следующий код:

Box

box1 = new Box();

Если мы попробуем сделать так, то получим ошибку что конструктор Box() не определен. Поэтому, если мы добавляем свои конструкторы в класс, то, если хотим использовать конструктор по-умолчанию (без аргументов), то должны его тоже объявить.

Вот мы потихоньку и подошли к понятию перегрузки методов и конструкторов, а так же для чего и как это используется. Этот момент мы тоже рассмотрим чуть позже, так как для порядка, нам надо рассмотреть еще одну тему – это деструкторы. Ну как бы если есть конструкторы, то должны быть и деструкторы, а вот их и нет 🙂 в Java.

Поскольку распределение памяти для объектов осуществляется динамически посредством операции new, может возникнуть вопрос, как уничтожаются такие объекты и как их память освобождается для последующего распределения. В некоторых языках, подобных C++, динамически распределенные объекты нужно освобождать вручную. В Java применяется другой подход. Освобождение памяти выполняется автоматически. Используемая для выполнения этой задачи технология называется сборкой мусора. Процесс проходит следующим образом: при отсутствии какихлибо ссылок на объект программа заключает, что этот объект больше не нужен, и занимаемую объектом память можно освободить. В Java не нужно явно уничтожать объекты, как это делается в C++. Во время выполнения программы сборка мусора выполняется только изредка (если вообще выполняется). Она не будет выполняться лишь потому, что один или несколько объектов существуют и больше не используются. Более того, в раз личных реализациях системы времени выполнения Java могут применяться различные подходы к сборке мусора, но в большинстве случаев при написании программ об этом можно не беспокоиться.

Метод finalize()

Иногда при уничтожении объект должен будет выполнять какое-либо действие. Например, если объект содержит какой-то ресурс, отличный от ресурса Java (вроде файлового дескриптора или шрифта), может требоваться гарантия освобождения этих ресурсов перед уничтожением объекта. Для подобных ситуаций Java предоставляет механизм, называемый финализацией. Используя финализацию, можно определить конкретные действия, которые будут выполняться непосредственно перед удалением объекта сборщиком мусора.

Чтобы добавить в класс средство выполнения финализации, достаточно определить метод finalize(). Среда времени выполнения Java вызывает этот метод непосредственно перед удалением объекта данного класса. Внутри метода finalize() нужно указать те действия, которые должны быть выполнены перед уничтожением объекта. Сборщик мусора запускается периодически, проверяя наличие объектов, на которые отсутствуют ссылки как со стороны какого-либо текущего состояния, так и косвенные ссылки через другие ссылочные объекты. Непосредственно перед освобождением ресурсов среда времени выполнения Java вызывает метод finalize() по отношению к объекту.

Важно понимать, что метод finalize() вызывается только непосредственно перед сборкой мусора. Например, он не вызывается при выходе объекта за рамки области определения. Это означает, что неизвестно, когда будет — и, даже будет ли вообще — выполняться метод finalize(). Поэтому программа должна предоставлять другие средства освобождения используемых объектом системных ресурсов и тому подобного. Нормальная работа программы не должна зависеть от метода finalize().

Короче! Хоть метод finalize() и существует, пользоваться им категорически не рекомендуется!

Все! На этом введение в классы закончено! 🙂 Но это только начало 🙂 Самое начало 🙂

Мультимедийный ноутбук XPS

Мультимедийный ноутбук XPS

ᲨПроизводительность

Во-первых, на выбор предлагается четыре новых поколения двухъядерных процессоров Intel®, каждый из которых обеспечивает значительно более высокую производительность системы по сравнению с исходной серией двухъядерных процессоров Intel®. И, чтобы соответствовать этому, одна из лучших графических карт в мире — NVIDIA® GeForceTM — с памятью до 4 ГБ, которая не разочарует.

Экран и звук

Дисплей, демонстрирующий эту мощность и производительность, — это широкоэкранный дисплей UltraSharpTM WUXGA (1920×1200) TrueLifeTM с диагональю 17 дюймов. Он на 30 % ярче предыдущего монитора Dell XPS и отображает на 77 % больше контента, чем стандартный 17-дюймовый WXGA+. В результате вы получаете экран с удивительно четким текстом и изображениями, поддерживаемый звуковой системой со встроенным сабвуфером для обеспечения громкости и четкости.

Блюрэй

Конечно, навороченный монитор и аудиосистема — это именно то, что вам нужно, особенно если вы решили установить оптический диск Blu-Ray (BD). Благодаря улучшениям графики NVIDIA® PureVideoTM диски Blu-ray обеспечивают гораздо лучшее качество изображения, чем обычные DVD.

Тем не менее, ваши DVD не являются дополнительными для XPS M1710. Вы по-прежнему можете воспроизводить их и использовать для записи текста и изображений. Однако Blu-ray Disc хранит до 25 ГБ данных по сравнению с 4,7 ГБ на DVD.

MediaDirect™

Вы можете просматривать материал на этом оптическом диске, Blu-ray или DVD, не подключая XPS M1710 обычным способом. Вместо этого при выключенном компьютере кнопка Dell MediaDirectTM позволяет просматривать данные на жестком диске, дисках, USB-порте и носителях, хранящихся в слоте для карты 5-в-1. А для безопасности есть способ остановить нежелательный доступ с паролем.

Другие динамические характеристики

Эта технология впечатляет, но у XPS M1710 есть и кое-что уникальное: фоновая подсветка. Вы можете комбинировать 16 мигающих и пульсирующих цветов с музыкой или просто включать свет, чтобы создавать предустановленные эффекты, такие как погоня и затухание. Также к играм можно подключить систему освещения, чтобы, например, огонь на экране излучал красно-оранжевое свечение.

Эти спецэффекты могут указывать на то, что XPS M1710 — хрупкая машина. Скорее, это ноутбук, использующий технологию Dell RoadReadyTM. Прочный металлический корпус черного цвета и внутренний амортизатор StrikeZoneTM, защищающий жесткий диск, обеспечивают долговечность XPS M1710.

Все эти особенности, безусловно, привлекают внимание. И вместе они превращают ноутбук XPS M1710 во что-то, чего можно с нетерпением ждать.

Как создать и вызвать метод в Java

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

Java предоставляет два типа методов: предопределенные методы и пользовательские методы.Эта запись обеспечит подробное понимание определяемых пользователем методов. В этом руководстве будут рассмотрены следующие концепции определяемых пользователем методов Java:

Итак, начнем!

Что такое метод Java

Метод Java — это блок кода/операторов, который может принимать или не принимать некоторые входные данные и возвращать некоторые выходные данные. Метод должен быть объявлен внутри класса. Чтобы создать метод Java, мы должны следовать правильному синтаксису, как описано ниже.

Базовый синтаксис методов Java

В приведенном ниже фрагменте показано, как объявить метод в Java:

Модификатор Спецификатор /access указывает тип доступа к методу, а java предоставляет четыре типа модификаторов i.е. по умолчанию, общедоступный, частный и защищенный.

  • Публичный модификатор указывает, что метод доступен для всех классов/дочерних классов,
  • Модификатор private указывает, что метод доступен только тем классам, в которых он указан,
  • Модификатор protected указывает, что метод доступен только в указанном пакете.

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

Тип возвращаемого значения указывает, какой тип данных будет возвращен методом в приведенном выше фрагменте. Используется void, что означает, что тип данных не будет возвращен.

Наконец, Имя метода — это имя метода, с помощью которого мы можем его вызвать.

Создание метода в Java

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

Давайте рассмотрим приведенный ниже пример, чтобы понять, как создать метод в Java:

Пример

Фрагмент ниже создает метод для нахождения квадрата числа:

открытый класс HelloWorld {

  static void findSquare(){

  int num, sqr;

  Сканирование сканера = новый сканер (System.in);

  System.out.print(«Введите число: «);

num = scan. nextInt();

  sqr = число * число;

  System.out.println(«Квадрат  » + num + »  is :  » + sqr);

 }

В классе «HelloWorld» мы создали метод findSquare().Класс сканера используется для получения ввода от пользователя, кроме того, функциональность для нахождения квадрата числа определяется в методе findSquare().

Как вызвать метод в Java

После того, как метод создан, мы можем вызвать его, и для этого нам нужно написать имя метода, за которым следует (), как показано ниже:

public static void main(String[] args) {

 findSquare();

}

Полный код и его вывод показаны в приведенном ниже фрагменте:

Пользователь ввел число «12» и в результате получил квадрат «144»
.

Заключение

Чтобы создать метод, мы должны указать модификатор доступа и тип возвращаемого значения, а затем имя метода, вся функциональность будет определена в методе. Чтобы вызвать метод, мы должны указать имя метода, за которым следуют круглые скобки (). В этой статье показано, как создать и вызвать метод в Java с помощью простого и точного примера.

Когда делать метод статическим в Java? Пример

Сделать метод статическим в Java — важное решение.Несмотря на то, что ключевое слово static является одним из фундаментальных понятий, программисты часто путаются, делая конкретный метод статическим или нет. В программировании на Java основной причиной создания статического метода является удобство . Вы можете вызвать статический метод без создания какого-либо объекта, просто используя имя его класса. Поэтому, если вам нужен метод, который вы хотите вызывать непосредственно по имени класса, сделайте этот метод статическим. Вспомогательные классы, например. java.lang.Math или StringUtils — хорошие примеры классов, использующих статические методы.Прежде чем сделать метод статическим, вы также должны изучить ограничения статических методов, поскольку вы не можете переопределить статические методы в Java.

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

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

Что делает статический метод в Java?

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

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


Правила для статического метода в Java

Не существует жестких и быстрых, хорошо написанных правил, чтобы решить, когда делать метод статическим или нет. Но есть несколько наблюдений, основанных на опыте, которые не только помогают сделать метод статическим, но и учат, когда использовать статический метод. в Яве.Вам следует подумать о том, чтобы сделать метод статическим в Java:

1) Если метод не изменяет состояние объекта или не использует какие-либо переменные экземпляра.

2) Вы хотите вызвать метод, не создавая экземпляр этого класса.

3) Метод является хорошим кандидатом на роль статического, если он работает только с предоставленными ему аргументами, например. public int factorial(int number){}, этот метод работает только с числом, предоставленным в качестве аргумента.

4) Вспомогательные методы также являются хорошими кандидатами на роль статических. e.грамм. StringUtils.isEmpty(текст строки), это служебный метод для проверки, является ли строка пустой или нет. 5) Если функция метода останется статической в ​​иерархии классов, например. Метод equals() не является хорошим кандидатом для создания статики, потому что каждый класс может переопределить равенство.

Когда использовать статический метод в Java? Теперь, когда мы знаем преимущества и ограничения статического метода в Java, мы видим пару сценариев, в которых можно использовать статические методы. Фабричный шаблон проектирования обеспечивает хорошее использование статического метода.Вы можете использовать статический метод для создания экземпляров класса. Даже книга Effective Java советует использовать статический фабричный метод, несколько примеров из них в библиотеке Java создают пул потоков из класса Executors.

Executors предоставляет множество статических методов для создания различных типов пулов потоков, например. public static ExecutorService newCachedThreadPool(), public static ExecutorService newFixedThreadPool(int nThreads) и т. д.

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

Статический метод также можно комбинировать с переменными аргументами для создания набора явных элементов, например. EnumSet.of(E сначала, E… остальное). Помимо этого, если вы откроете библиотеку Apache commons-lang, вы найдете шаблон класса utils, например. StringUtils, ArrayUtils, который предоставляет служебные методы для работы со строками и массивами.

Еще одно интересное использование статического метода, которое я видел, — это метод valueOf() внутри разных классов значений, например.грамм. java.lang.String, хотя это также пример фабричного метода, это также хороший способ преобразовать один тип в другой.

Например, valueOf() также можно использовать для преобразования строки в целое число в Java. Короче говоря, имеет смысл использовать статические методы:

1) Наряду с творческим шаблоном проектирования, например. Фабрика и Синглтон.

2) Как служебный метод, работающий с аргументами.

3) Инструмент преобразования, например. ценность().

Это все о , когда делать метод статическим в Java .Мы видели преимущества и ограничения статического метода и несколько примеров статических методов из JDK. Примеры JDK также помогут вам решить, когда использовать статический метод в Java.


Метод получения и возврата строковых данных в Java

В Java char[] , String , StringBuffer и StringBuilder используются для хранения, получения и возврата строковых данных. Один из вопросов, который может прийти вам на ум, звучит так: «Как лучше всего определить метод для получения строковых данных и возврата строковых данных в Java?»

У нас есть четыре способа получить и вернуть строковые данные:
1) char [] /byte []
2) String
3) StringBuilder
4) StringBuffer

  char[]   не лучший вариант, поскольку он работает с массивом.Получение и возврат строковых данных с использованием   char[]   – не лучший вариант.

Второй способ — использовать класс String. Класс String неизменяем. При каждой модификации он создает новый объект. Таким образом, мы создаем много новых объектов, которые на самом деле не требуются для нашей программы. Поэтому не рекомендуется работать только с классом String.

Эта Java-программа для реверсирования String показывает, что для небольшой задачи, которая предназначена для реверсирования «Hello», она создает пять объектов.

Выход:

Для больших задач будет создаваться так много объектов строкового класса, которые на самом деле не нужны в программе. Таким образом, мы теряем память.

Использование только StringBuilder для получения и возврата строковых данных

Третий способ — использование класса StringBuilder . Класс   StringBuilder является изменяемым. При каждой модификации он не создает новый объект, а сохраняет его в том же объекте. Но возникает проблема: поскольку мы используем StringBuilder для получения аргумента, вызывающий метод должен передать значение в форме StringBuilder .Опять же, мы возвращаем строковые данные, используя StringBuilder , поэтому вызывающий метод должен хранить возвращаемое значение как StringBuilder .

Разработчик вызывающего метода должен создать новый объект StringBuilder и преобразовать строковые данные в StringBuilder только потому, что вызываемый метод, принимающий аргумент, находится в форме StringBuilder , а не в форме String. Точно так же им необходимо создать объект StringBuilder только потому, что вызываемый метод возвращает строковые данные в виде StringBuilder .

Выход:

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

Использование только StringBuffer для получения и возврата строковых данных в Java

Четвертый способ — использование класса StringBuffer. Класс StringBuffer аналогичен классу StringBuilder. В реализации разницы не будет. Класс StringBuffer синхронизирован, поэтому он лучше подходит для многопоточного приложения, а не для однопоточного приложения. Класс StringBuffer также создаст единственный объект как StringBuilder, но нам нужно написать дополнительную логику для преобразования StringBuffer в String и String в StringBuffer.

Введите строку: Hello
Адрес версии: 980546781
Адрес версии: 980546781
Адрес версии: 980546781
Адрес версии: 980546781
Адрес версии: 9805467811

Это также не рекомендуется.


Тогда как нам написать метод?

Мы должны принять и вернуть аргумент как строковый объект класса. Таким образом, в вызывающем методе нам не нужно писать какую-либо дополнительную логику в вызывающем методе.

В этом методе мы должны преобразовать объект класса String в класс StringBuilder , обработать логику класса StringBuilder, а затем преобразовать объект класса StringBuilder в объект класса строки и вернуть его.

Принять аргумент как объект класса String => преобразовать объект класса String в объект класса StringBuilder => обработать бизнес-логику => преобразовать объект класса StringBuilder в объект класса String => вернуть объект класса String.

Для преобразования класса String в класс StringBuilder существует только один способ — использование конструктора StringBuilder(String).

Для преобразования StringBuilder в объект класса String есть три способа: —

  • toString()
  • значениеOf()
  • Строка (StringBuilder)

Среди этих трех способов первый, т. е. использование метода toString(), лучше двух оставшихся.

Введите строку: Привет

Как правильно реализовать метод equals в Java

В SitePoint мы всегда стремимся расширить спектр тем, которые мы освещаем.В последнее время мы нацелились на изучение мира Java. Если вы сильный Java-разработчик и хотите внести свой вклад в наше освещение, свяжитесь с несколькими идеями для статей, которые вы хотели бы написать.

Фундаментальным аспектом любого класса Java является его определение равенства. Он определяется методом класса равно , и для правильной реализации необходимо учитывать несколько моментов. Давайте проверим их, чтобы мы поняли это правильно!

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

Идентичность против равенства

Взгляните на этот фрагмент кода:

  String some = "строка";
Строка другая = "другая строка";
  

У нас есть две строки, и они явно разные.

А эти двое?

  String some = "строка";
Строка «другое» = «некоторые»;
логический идентичный = некоторые == другие;
  

Здесь у нас есть только один экземпляр String, и некоторые и другие ссылаются на него. В Java мы говорим, что некоторые и другие являются идентичными и, соответственно, идентичными возвращают логическое значение, которое равно true .

А этот?

  String some = "строка";
String other = "какая-то строка";
логический идентичный = некоторые == другие;
  

Теперь некоторые и другие указывают на разные экземпляры и больше не идентичны, поэтому идентично ложно. (В этой статье мы проигнорируем интернирование строк; если это вас беспокоит, предположим, что каждый строковый литерал был обернут в файл new String(...) .)

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

  String some = "строка";
String other = "какая-то строка";
логическое равно = some. equals(other);
  

Здесь равно верно .

Идентификатор переменной (также называемый Ссылочное равенство ) определяется содержащейся в ней ссылкой.Если две переменные содержат одну и ту же ссылку, они идентичны . Это проверяется с помощью == .

Равенство переменной определяется значением, на которое она ссылается. Если две переменные ссылаются на одно и то же значение, они равны . Это проверяется с помощью равно .

Но что значит «то же самое значение»? На самом деле именно реализация равна определяет «одинаковость». Метод равно определен в объекте , и, поскольку все классы наследуются от него, у всех есть этот метод.

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

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

Например:

  Список список = Массивы.asList("а", "б", "в");
логическое значение содержит = список.содержит ("b");
  

Переменная содержит is true , потому что хотя экземпляры "b" не идентичны, они равны.

(Это также момент, когда hashCode вступает в игру.)

Мысли о равенстве

Любая реализация равна должна соответствовать определенному контракту, иначе равенство класса будет плохо определено и произойдут всевозможные неожиданные вещи.Мы рассмотрим формальное определение через минуту, но давайте сначала обсудим некоторые свойства равенства.

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

  1. Одно свойство настолько тривиально, что едва ли стоит упоминать: Каждая вещь равна самой себе. Дух.
  2. Есть и другое, не намного более вдохновляющее: если одно равно другому, то и другое равно первому.Понятно, что если мой ноутбук равен вашему, то ваш равен моему.
  3. Вот это интереснее: если у нас есть три вещи и первая и вторая равны, а вторая и третья равны, то первая и третья тоже равны. Опять же, это очевидно в нашем примере с ноутбуком.

Это было бесполезное упражнение, верно? Не так! Мы только что рассмотрели некоторые основные алгебраические свойства отношений эквивалентности. Нет, подожди, не уходи! Это уже все, что нам нужно. Потому что любое отношение, обладающее тремя указанными выше свойствами, можно назвать равенством.

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

равно Контракт

Контракт = — это не более чем формализация того, что мы видели выше.
Цитировать источник:

Метод equals реализует отношение эквивалентности непустых ссылок на объекты:

  • Это рефлексивное : для любого ненулевого опорного значения x , x.equals(x) должен возвращать true .
  • Это симметричный : для любых ненулевых ссылочных значений x и y , x.equals(y) должен возвращать true тогда и только тогда, когда y.equals(x) возвращает true.
  • Это транзитивно : для любых ненулевых ссылочных значений x , y и z , если x.equals(y) возвращает true и y.0equals1 возвращает y. 0equals1 . правда , затем х.equals(z) должен возвращать true .
  • Это непротиворечиво : для любых ненулевых ссылочных значений x и y многократные вызовы x.equals(y) последовательно возвращают true или последовательно возвращают false , при условии, что информация не используется в равняется сравнениям объектов.
  • Для любого ненулевого значения ссылки x , x.equals(null) должно возвращать false .

К настоящему времени первые три должны быть вам хорошо знакомы. Другие моменты носят скорее технический характер: без согласованности структуры данных ведут себя хаотично, и равенство нулю не только не имеет смысла, но и усложнило бы многие реализации.

Реализация

равна

Для класса Person со строковыми полями firstName и lastName это был бы общий вариант реализации равно :

  @Переопределение
общедоступное логическое значение равно (объект o) {
    
    если (это == о)
        вернуть истину;
    
    если (о == ноль)
        вернуть ложь;
    
    если (getClass() != о. получитькласс())
        вернуть ложь;
    Человек-человек = (Человек) о;
    
    вернуть Objects.equals(firstName, person.firstName)
            && Objects.equals(lastName, person.lastName);
}
  

Давайте пройдемся по порядку.

Подпись

Очень важно, что равняется и принимает Объект ! В противном случае возникает непредвиденное поведение.

Например, предположим, что мы реализуем equals(Person) следующим образом:

  public boolean equals(Person person) {
    вернуть объекты.равно(firstName, person.firstName)
            && Objects.equals(lastName, person.lastName);
}
  

Что происходит в простом примере?

  Person elliot = new Person("Эллиот", "Алдерсон");
Человек mrRobot = новый Человек("Эллиот", "Алдерсон");
логическое значение равно = elliot.equals(mrRobot);
  

Тогда равно и будет истинным . А сейчас?

  Person elliot = new Person("Эллиот", "Алдерсон");
Object mrRobot = new Person("Эллиот", "Алдерсон");
логическое равно = Эллиот. равно (мрробот);
  

Теперь ложь . Wat?! Возможно, не совсем то, что мы ожидали.

Причина в том, что Java вызвал Person.equals(Object) (как унаследованный от Object , который проверяет личность). Почему?

Стратегия Java для выбора перегруженного метода для вызова основана не на типе времени выполнения параметра, а на его объявленном типе. (Что хорошо, потому что в противном случае статический анализ кода, например иерархия вызовов, не работал бы.) Итак, если mrRobot объявлен как Object , Java вызывает Person.equals(Object) вместо нашего Person.equals(Person) .

Обратите внимание, что большая часть кода, например, все коллекции, обрабатывает наших людей как объекты и, таким образом, всегда вызывает equals(Object) . Поэтому нам лучше убедиться, что мы предоставляем реализацию с этой подписью! Конечно, мы можем создать специализированную реализацию равно и вызывать ее из нашей более общей, если нам так больше нравится.

Самопроверка

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

java
если (это == o)
вернуть true;

Может показаться, что он должен реализовать рефлексивность, но проверки ниже были бы очень странными, если бы они также не делали этого.

Проверка нуля

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

  если (о == ноль)
    вернуть ложь;
  

На самом деле его можно включить в следующую проверку, например:

  если (o == null || getClass() != o.getClass())
    вернуть ложь;
  

Проверка типа и отливка

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

  если (getClass() != o.getClass())
    вернуть ложь;
Человек-человек = (Человек) о;
  

Наша реализация использует getClass , который возвращает классы, к которым принадлежат этот и или . Он требует, чтобы они были идентичны! Это означает, что если бы у нас был класс Employee extends Person , то Person.equals(Employee) никогда не вернул бы true — даже если бы оба имели одинаковые имена.

Это может быть неожиданно.

То, что расширяющий класс с новыми полями плохо сравнивается, может быть разумным, но если это расширение только добавляет поведение (возможно, ведение журнала или другие нефункциональные детали), оно должно быть способно сопоставлять экземпляры своего супертипа. Это становится особенно актуальным, если фреймворк запускает новые подтипы во время выполнения (например, Hibernate или Spring), которые никогда не могут быть равны экземплярам, ​​которые мы создали.

Альтернативой является оператор instanceof :

  if (!(o instanceof Person))
    вернуть ложь;
  

Экземпляры подтипов Person проходят эту проверку.Следовательно, они продолжают сравнение полей (см. ниже) и могут оказаться равными. Это решает проблемы, о которых мы упоминали выше, но открывает новую банку червей.

Произнесите Сотрудник расширяет Person и добавляет дополнительное поле. Если он переопределяет реализацию equals , он наследуется от Person и включает дополнительное поле, тогда person.equals(employee) может быть true (из-за instanceof ), но employee.equals(person) не может (потому что человека пропускают это поле).Это явно нарушает требование симметрии.

Кажется, из этого есть выход: Employee.equals может проверить, сравнивается ли он с экземпляром с этим полем, и использовать его только тогда (это иногда называется сравнением слайсов ).

Но это тоже не работает, потому что нарушает транзитивность:

  Person foo = new Person("Mr", "Foo");
Сотрудник fu = новый Сотрудник("Мистер", "Фу", "Маркетинг");
Сотрудник fuu = новый Сотрудник("Мистер", "Фу", "Инженер");
  

Очевидно, что все три экземпляра имеют одно и то же имя, поэтому foo.equals(fu) и foo.equals(fuu) равны true . По транзитивности fu.equals(fuu) также должно быть true , но это не так, если в сравнение включено третье поле, по-видимому, отдел.

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

Таким образом, мы заканчиваем с двумя альтернативами:

  • Используйте getClass и помните, что экземпляры типа и его подтипы никогда не могут быть равными.
  • Использовать экземпляр из , но сделать равным final, потому что нет возможности правильно переопределить его.

Какой из них имеет больше смысла, зависит от ситуации. Лично я предпочитаю экземпляр экземпляра , потому что его проблемы (не могут включать новые поля в унаследованные классы) возникают на сайте объявления, а не на сайте использования.

Полевое сравнение

Вау, это было много работы! И все, что мы сделали, это решили некоторые угловые случаи! Итак, давайте, наконец, перейдем к сути теста: сравнению полей.

Это довольно просто. В подавляющем большинстве случаев все, что нужно сделать, это выбрать поля, которые должны определять равенство классов, а затем сравнить их. Используйте == для примитивов и равно для объектов.

Если какое-то из полей может быть пустым, дополнительные проверки значительно снижают читабельность кода:

  возврат (firstName == person. firstName
        || firstName != null && firstName.equals(person.firstName))
    && (фамилия == человек.Фамилия
            || lastName != null && lastName.equals(person.lastName))
  

А это уже использует тот неочевидный факт, что null == null равно true .

Гораздо лучше использовать вспомогательный метод Java Objects.equals (или, если вы еще не используете Java 7, Objects.equal Guava):

  вернуть Objects.equals(firstName, person.firstName)
        && Objects.equals(lastName, person.lastName);
  

Он выполняет точно такие же проверки, но гораздо более читаем.

Резюме

Мы обсудили разницу между тождеством (должна быть одна и та же ссылка; проверяется с помощью == ) и равенством (могут быть разные ссылки на «одно и то же значение»; проверяется с помощью , равного ) и приступили к внимательному рассмотрению. как реализовать равно .

Давайте соберем эти части вместе:

  • Не забудьте переопределить equals(Object) , чтобы наш метод всегда вызывался.
  • Включите самопроверку и проверку нуля для раннего возврата в простых пограничных случаях.
  • Используйте getClass , чтобы разрешить подтипам их собственную реализацию (но без сравнения между подтипами), или используйте instanceof и сделайте равным final (и подтипы могут быть равными).
  • Сравните нужные поля, используя Objects.equals .

Или пусть ваша IDE сгенерирует все это для вас и отредактирует там, где это необходимо.

Заключительные слова

Мы видели, как правильно реализовать равно (и скоро рассмотрим hashCode ).Но что, если мы используем классы, над которыми у нас нет контроля? Что, если их реализации этих методов не соответствуют нашим потребностям или просто неверны?

LibFX спешит на помощь! (Отказ от ответственности: я являюсь автором. ) Библиотека содержит преобразующие коллекции, и одна из их функций — позволить пользователю указать нужные ему методы equals и hashCode .

Начало работы | Создание приложения с помощью Spring Boot

Если вы создаете веб-сайт для своего бизнеса, вам, вероятно, потребуется добавить некоторые службы управления.Spring Boot предоставляет несколько таких сервисов (таких как работоспособность, аудит, bean-компоненты и т. д.) с помощью модуля привода.

Если вы используете Gradle, добавьте следующую зависимость в файл build.gradle :

  реализация 'org.springframework.boot: spring-boot-starter-actuator'  

Если вы используете Maven, добавьте в файл pom.xml следующую зависимость:

  <зависимость>
org.springframework.boot
пружинный-загрузочный-стартер-привод
  

Затем перезапустите приложение. Если вы используете Gradle, выполните следующую команду в окне терминала (в полном каталоге ):

Если вы используете Maven, выполните следующую команду в окне терминала (в полном каталоге ):

Вы должны увидеть, что в приложение добавлен новый набор конечных точек RESTful. Это службы управления, предоставляемые Spring Boot. В следующем листинге показан типичный вывод:

  management.endpoint.configprops-org.springframework.boot.actuate.autoconfigure.context.properties.ConfigurationPropertiesReportEndpointProperties
management.endpoint.env-org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointProperties
management.endpoint.health-org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties
management.endpoint.logfile-org.springframework.boot.actuate.autoconfigure.logging.LogFileWebEndpointProperties
management.endpoints.jmx-org.springframework.boot.actuate.autoconfigure. endpoint.jmx.JmxEndpointProperties
management.endpoints.web-org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties
management.endpoints.web.cors-org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties
management.health.diskspace-org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthIndicatorProperties
management.info-org.springframework.boot.actuate.autoconfigure.info.InfoContributorProperties
management.metrics-org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties
management.metrics.export.simple-org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleProperties
management.server-org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties  

Привод выставляет следующее:

Существует также конечная точка /actuator/shutdown , но по умолчанию она видна только через JMX. Чтобы включить его в качестве конечной точки HTTP, добавьте management.endpoint.shutdown.enabled=true в файл application.properties и откройте его с помощью management.endpoints.web.exposure.include=health,info,shutdown . Однако вам, вероятно, не следует включать конечную точку выключения для общедоступного приложения.

Вы можете проверить работоспособность приложения, выполнив следующую команду:

  $ curl localhost:8080/привод/здоровье
{"Статус":"ВВЕРХ"}  

Вы также можете попробовать вызвать завершение работы через curl, чтобы увидеть, что произойдет, если вы не добавили нужную строку (показанную в предыдущем примечании) в приложение .свойства :

  $ curl -X POST localhost:8080/привод/выключение
{"timestamp": 1401820343710, "ошибка": "не найдено", "статус": 404, "сообщение": "", "путь": "/actuator/shutdown"}  

Поскольку мы не включили его, запрошенная конечная точка недоступна (поскольку конечная точка не существует).

Дополнительные сведения о каждой из этих конечных точек REST и о том, как настроить их параметры с помощью файла application.properties src/main/resources ), см. в документации по конечным точкам.

Java Concurrency — Как создавать потокобезопасные системы | Гэвин Фонг | март 2022 г.

Покажу вам, как обеспечить потокобезопасную обработку данных с помощью синхронизированной, синхронизированной карты, Hashtable, ConcurrentMap

Фото Райана Стоуна на Unsplash

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

Давайте рассмотрим пример простой банковской службы ниже. Служба запускает вызов метода doTransaction() при получении запроса на транзакцию. Сначала он получает запись целевого счета и последний баланс из карты данных, после чего выполняется транзакция (например, ввод/вывод средств). Затем обновленный баланс сохраняется в записи учетной записи.

Банковская служба

Пример кода ниже является примером реализации банковской службы

Допустим, депозит в размере 10 долларов США и вывод 5 долларов США должны выполняться двумя отдельными потоками соответственно.При начальном балансе счета в 100 долларов США окончательный баланс аккаунта должен составить 105 долларов США после завершения двух транзакций.

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

Последовательное выполнение транзакций

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

Приведенная ниже последовательность выполнения показывает, что поток 2 получает остаток на счете в размере 100 долларов США, но баланс счета был позже обновлен потоком 1 для транзакции депозита. В результате в записи учетной записи сохраняется неправильный баланс счета в размере 95 долларов США, поскольку поток 2 выполняет транзакцию на основе устаревшего баланса.

Потоки выполняются с чередованием

Приведенный выше пример показывает, что системные дефекты из-за проблем с параллелизмом могут привести к серьезным последствиям. Параллельное программирование является важным навыком, когда речь идет о разработке веб-приложений и серверных служб.В отличие от приложений для настольных компьютеров или мобильных устройств, онлайн-системы обслуживают несколько входящих запросов одновременно с несколькими потоками, обращающимися к общему хранилищу данных.

На примере банковского сервиса я покажу вам следующие способы обработки состояния гонки в многопоточной среде путем управления доступом к общим данным:

  • Применить синхронизацию к вызову метода
  • Синхронизировано map wrapper
  • Hashtable
  • ConcurrentMap
  • Применить синхронизацию к блоку кода

Для приведенного выше списка методов я также покажу вам результаты нагрузочного теста и представлю сравнение производительности.

Язык программирования Java предоставляет ключевое слово synchronized в подписи метода. Это мощная функция, которая обеспечивает выполнение вызова метода одним потоком за раз. Другие потоки приостанавливаются в ожидании выполнения вызова метода.

Потоки выполняются doTransaction() один за другим, это решает проблему, устраняя возможность выполнения транзакции на основе устаревшего баланса.

Синхронизация всего вызова метода

HashMap — это реализация карты данных по умолчанию.Он не является потокобезопасным, поскольку не обеспечивает последовательный доступ к карте данных. Несколько потоков, обращающихся к HashMap , могут привести к несогласованности данных. Чтобы превратить HashMap в карту синхронизированных данных, служебный класс Collections предоставляет вызов метода synchronizedMap() , который просто преобразует карту входных данных в карту синхронизированных данных.

В этом примере кода показано использование Collections.synchronizedMap() для переноса карты данных учетной записи в карту синхронизированных данных.

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

Синхронизация карты доступа к данным

Эта опция использует ту же идею, что и опция 2. Она заключается в том, чтобы сделать карту данных учетной записи синхронизированной картой . Вместо использования Collections.synchronizedMap() для создания оболочки этот параметр заменяет HashMap на Hashtable . Hashtable является потокобезопасным, но не неэффективным.Вы увидите разницу в производительности в следующем разделе.

В то время как Hashtable является самой ранней реализацией карты данных со времен Java 1. 0, ConcurrentMap — это еще одна реализация карты данных с поддержкой потоков, представленная в версии 1.5. Вы можете рассматривать его как «улучшенную» версию Hashtable , поскольку она более эффективна и безопасна для потоков.

Ключевое слово Synchronized может применяться не только к сигнатуре метода, но и к блоку кода.Пример кода ниже демонстрирует использование синхронизированного блока для расчета баланса. Объект учетной записи извлекается из карты данных по номеру учетной записи, затем объект учетной записи предоставляется синхронизированному блоку в качестве входного параметра, так что только одному потоку разрешено запускать блок кода для каждого объекта учетной записи. Другими словами, синхронизация применяется для каждого уровня учетной записи.

Вместо одной очереди для выполнения транзакций этот параметр поддерживает очередь для каждой учетной записи.Поэтому транзакции разных счетов могут выполняться параллельно. Несомненно, этим вариантом достигается высокая производительность исполнения транзакций.

Синхронизация по блоку кода на аккаунт

Какой вариант самый эффективный? Давайте посмотрим на результат нагрузочного теста ниже. Дизайн нагрузочного теста заключается в запуске 3 потоков для выполнения в общей сложности 1 миллиона транзакций. Вариант синхронизации по кодовому блоку является самым быстрым, так как позволяет одновременное выполнение транзакций на разных счетах.Второй лучший вариант — это синхронизация на уровне метода с последующим использованием ConcurrentMap . Более того, использование Hashtable не рекомендуется из-за его низкой производительности. Хотя использование Collections.synchronizedMap() удобно, банковское обслуживание с этим вариантом является самым медленным среди всех вариантов. Вероятно, использование оболочки приводит к накладным расходам, поскольку добавляет слой для синхронизации перед базовой картой данных.

Несмотря на наилучшую эффективность синхронизации блока кода, существует компромисс, поскольку реализация более сложна.Требуются дополнительные усилия по проектированию и тестированию блокировки объекта, чтобы убедиться, что реализация выполнена правильно.

Результаты нагрузочного теста

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

GitHub Repository

Получите исходный код из этого репозитория, вы найдете реализацию всех вариантов.

Comments