solution java что это
Class’ные Class’ы
Вводная
Немного деталей
Если Вы java-эксперт, примеры кода для Вас будут скучноватыми, а в остальном, как всегда. В свое время мне было очень интересно, что в java 7 снаружи и под капотом, как устроен формат файла class и прочее. Мне пришлось познакомиться вот с этими документами. От туда я подчеркнул почти все идеи для этой статьи. Тем не менее, я заранее прошу прощения за неточности в терминологии у фундаментальных теоретиков и опытных экспертов. На некторые вопросы я не буду давать ответы из-за их очевидности или легкого поиска ответа.
И так первый вопрос: ‘А какие бывают типы и виды классов в java 7?’ Большинство ответит правильно, но некоторые — неполностью. Очень часто забывают упомянуть про локальные классы.
Локальный класс
Я не нашел быстро хорошего определения локального класса на Русском языке, а с Английским проблемы, поэтому своими словами: ‘Локальный класс — это внутренний и вложенный именованный класс, не являющийся членом другого класса и объявление которого осуществляется внутри блока кода или метода’. Немного запутанно? На примере все просто:
Я просмотрел очень много кода за свою жизнь, но ни разу не встретил явных именованных деклараций класса внутри метода. Может просто не повезло. А Вы встречали? Но когда я решил собрать статистику по типам и видам классов, то обнаружил, что в rt.jar локальные классы присутствуют и более того, используются в таком небезызвестном классе как java.lang.Package. Век живи — век учись. Еще есть интересное утверждение: ‘Анонимный класс — это локальный класс без имени’. Для экспертов вопрос: ‘Так ли это?’
Класс аннотаций
Уже не осталось людей, которые бы ни разу не писали классы типа аннотации. И сразу маленький пример.
Но тем не менее, здесь есть чему удивиться. Как вы думаете, ниже представлен валидный код?
На самом деле ничего сложного нет. Но продолжим, а как Вам такой пример?
Ответы здесь все простые — это рабочие примеры кода, так как по факту, под капотом interface = interface, с небольшими оговорками, поэтому все, что можно писать в interface можно и в аннотациях (опять же с оговорками). Наследование в коде от класса типа аннотация я встречал в тестах. Про аннотации у меня все, но есть маленький пример аннотированного типа массива строк и формы его объявления:
Надеюсь я Вас не утомил. Но если это не так, тогда следующий абзац специально для Вас.
Обычный класс
Очень сложно удивить кого-либо информацией об обычном классе (исключая примеры с дженериками). Как я ни старался, найти что-то значимое не смог. Но есть у меня одна история-анекдот.
Однажды разработчику потребовалось написать утилитный класс для решения поставленной задачи. Вроде все сделал правильно, написал java-doc, тесты. Отправил патч на ревью.
Начальник-Михалыч посмотрел патч и сказал — ‘Все ок, но давай сделаем защиту от дурака — добавь приватный конструктор’. Как это водится, разработчику патч переделывать не хочется, а поверх нельзя, поэтому превозмогая себя и, переходя определенную грань субординации, разраб спросил: ‘А что у нас в компании, Михалыч, дураки работают или ты кого-то конкретно имеешь в виду?’. Но делать нечего, нужно переделывать, причем все просто — добавить приватный конструктор:
‘Готово’ — крикнул разраб, ‘Молодец’ — ответил Михалыч. Хотел он было нажать submit, как раздался звонок. В этот самый момент, начальник департамента, освободившись от важных дел решил тряхнуть стариной и ткнул в первый попавшийся патч для ревью. ‘О-о-о!’ — заверещал он. ‘Михалыч, Вы что код разучились писать? А где защита от деб*ла?’. Начальник департамента человек серьезный, поэтому Михалыч про себя: ‘Что у нас в компании, деб*лы работают или ты кого-то конкретно имеешь в виду?’. Угрюмый Михалыч заворачивает патч с пометкой добавить abstract к классу. Нижняя губа разраба затряслась.Шо опять?
По иронии судьбы в этот день в отдел пришел стажер и, получив свое первое задание, кинулся в бой. Его взгляд остановился на Utils, и на лице появились и восхищение и недоумение. Набравшись смелости, он громко задал свой первый искрометный вопрос: ‘Парни, а как можно наследоваться от класса, с приватным конструктором?’
Класс перечислений
Кого сейчас ими удивишь? Вот если бы лет 10 назад. Поэтому здесь немного вопросов по пониманию кода. Как вы думаете, есть ли разница в декларации следующих элементов перечисления и если есть, то почему?
Если вы знаете правильный ответ то следующий вопрос Вам покажется легким: ‘Что будет на консоле?’
Конечно, здесь все на поверхности — E3.SIMPLE и E4.SIMPLE это экземпляры анонимного класса этих енумов. Поэтому последние 2 вызова дадут false результат. Будьте внимательны, когда используете проверку на enum класс через isEnum().
Внутренний класс
Про внутренние классы информации очень много, как, что и с чем их едят. Но многие, кого я собеседовал не могли ответить на 2 вопроса. Прежде обратимся к примеру:
И первый вопрос: ‘Как получить доступ к полю myField класса InnerClassExample в конструкторе класса InnerInnerClass и возможно ли это?’ Второй вопрос: ‘Как создать экземпляр класса InnerInnerClass в методе main класса InnerClassCreate?
ZhekovIlya / Solution.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
package com.javarush.task.task36.task3608.view ; |
import com.javarush.task.task36.task3608.controller.Controller ; |
import com.javarush.task.task36.task3608.model.ModelData ; |
public interface View < |
void refresh ( ModelData modelData ); |
void setController ( Controller controller ); |
> |
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.
Старые песни о главном. Java и исходящие запросы
(Иллюстрация)
Одна из задач, с которой сталкиваются 99,9% разработчиков, — это обращение к сторонним endpoint’ам. Это могут быть как внешние API, так и «свои» микросервисы. Сейчас все и вся бьют на микросервисы, да. Получить или отправить данные просто, но иногда изобретают велосипеды. Можете назвать 5 способов реализации запросов на Java (c использованием библиотек и без)? Нет — добро пожаловать под кат. Да? Заходите и сравните 😉
0. Intro
Задача, которую мы будем решать, предельно проста: нам необходимо отправить запрос GET/POST и получить ответ, который приходит в формате JSON. Чтобы не писать очередной оригинальный микросервис, я воспользуюсь примером, который предоставляет набор endpoint’ов с некоторыми данными. Все примеры кода максимально упрощены, никаких хитросделанных кейсов с auth-токенами и заголовками тут не будет. Только POST и GET, GET и POST, и так 5 раз или около того.
Итак, поехали.
1. Built-in Java solution
Было бы странно, если бы поставленную задачу нельзя было решить без использования сторонних библиотек. Конечно, можно! Но грустно. Пакет java.net, а именно HttpURLConnection, URL и URLEnconder.
Для отправки запроса, что GET, что POST, необходимо создать объект URL и открыть на его основе соединение:
Далее необходимо сдобрить соединение всеми параметрами:
И получить InputStream, откуда уже прочитать все полученные данные.
И, собственно, вот такой ответ мы получим (он будет одинаков для всех последующих примеров, ибо мы работаем с одними и теми же endpoint’ами):
В случае с POST-запросом все немного сложнее. Мы же хотим не только получить ответ, но и передать данные. Для этого нам нужно их туда положить. Документация нам говорит что это может сработать следующим образом:
Где getParamsString это простой метод, перегоняющий Map в String, содержащие пары «ключ-значение»:
При успешном создании мы получим объект обратно:
Ссылочка на source, который можно запустить.
2. Apache HttpClient
Если уйти в сторону от встроенных решений, то первое, на что мы наткнемся — HttpClient от Apache. Для доступа нам понадобится сам JAR-файл. Или, так как я использую Maven, то соответствующая зависимость:
И то, как выглядят запросы с использованием HttpClient’a, уже намного лучше (source):
Мы получили те же данные, но написали при этом вдвое меньше кода. Интересно, куда еще могут завести поиски в таком, казалось бы, базовом вопросе? Но у Apache есть еще один модуль, решающий нашу задачу.
3. Apache Fluent API
И уже с использованием Fluent Api наши вызовы становятся намного читабельнее (source):
И как бонус — пример. Если мы хотим передавать данные в боди не как форму, а как всеми любимый JSON:
По сути вызовы схлопнулись в одну строчку кода. Как по мне, это намного более дружелюбно по отношению к разработчикам, чем самый первый способ.
4. Spring RestTemplate
Что же дальше? Дальше опыт меня завел в мир Spring. И, что не удивительно, у спринга тоже имеются инструменты для решения нашей простенькой задачи (странно, правда? Задача, даже не так — потребность! — базового уровня, а решений зачем-то больше одного). И первое же решение (базовое), которое вы найдете в экосистеме Spring, это RestTemplate. И для этого нам нужно тянуть уже немалую часть всего зоопарка. Так что если вам нужно отправить запрос в НЕспринговом приложении, то ради этого лучше не тянуть всю кухню. А если спринг уже есть, то почему бы и да? Как притянуть все, что необходимо для этого, можно посмотреть здесь. Ну а собственно GET-запрос с использованием RestTemplate выглядит следующим образом:
Гуд. НО! Работать со строкой уже не хочется, тем более есть возможность получать не строки, а готовые объекты, которые мы ожидаем получить! Создаем объект Post:
Здесь:
Builder, Getter, Setter — сахар от Lombok, чтобы не писать все руками. Да, вот она, лень-матушка.
JsonIgnoreProperties — чтобы в случае получения неизвестных полей не вылетать в ошибку, а использовать те поля, которые нам известны.
Ну и toString, чтобы выводить наши объекты в консоль, и это можно было прочитать. Ну и собственно наши GET- и POST- запросы перевоплощаются в (source):
И у нас уже в руках объекты, а не строка, которую надо разбирать самостоятельно.
Кул. Теперь мы можем написать некоторую обертку вокруг RestTemplate, чтобы запрос строился корректно. Выглядит не так уж плохо, но, как по мне, это можно еще улучшить. Чем меньше кода пишется, тем меньше вероятность ошибки. Все же знают, что основная проблема зачастую PEBCAK (Problem Exists between Chair and Keyboard)…
5. Spring Feign
И тут на сцену выходит Feign, который входит в состав Spring Cloud. Сначала добавим к уже добавленному ранее спринговому окружению Feign-зависимость:
По сути все, что надо, это объявить интерфейс и сдобрить его хорошей жменькой аннотаций. Особенно данный подход будет симпатичен тем, кто пишет контроллеры с использованием спринга.
Вот что нам надо сделать для отправки запросов посредством Feign (source).
Красота, не правда ли? И да, те модели данных, которые мы писали для RestTemplate, отлично переиспользуются здесь.
6. Conclusion
Инструменты для запуска и разработки Java приложений, компиляция, выполнение на JVM
Ни для кого не секрет, что на данный момент Java — один из самых популярных языков программирования в мире. Дата официального выпуска Java — 23 мая 1995 года.
Эта статья посвящена основам основ: в ней изложены базовые особенности языка, которые придутся кстати начинающим “джавистам”, а опытные Java-разработчики смогут освежить свои знания.
* Статья подготовлена на основе доклада Евгения Фраймана — Java разработчика компании IntexSoft.
В статье присутствуют ссылки на внешние материалы.
1. JDK, JRE, JVM
Java Development Kit — комплект разработчика приложений на языке Java. Он включает в себя Java Development Tools и среду выполнения Java — JRE (Java Runtime Environment).
Java development tools включают в себя около 40 различных тулов: javac (компилятор), java (лаунчер для приложений), javap (java class file disassembler), jdb (java debugger) и др.
Среда выполнения JRE — это пакет всего необходимого для запуска скомпилированной Java-программы. Включает в себя виртуальную машину JVM и библиотеку классов Java — Java Class Library.
JVM — это программа, предназначенная для выполнения байт-кода. Первое преимущество JVM — это принцип “Write once, run anywhere”. Он означает, что приложение, написанное на Java, будет работать одинаково на всех платформах. Это является большим преимуществом JVM и самой Java.
До появления Java, многие компьютерные программы были написаны под определенные компьютерные системы, а предпочтение отдавалось ручному управлению памятью, как более эффективному и предсказуемому. Со второй половины 1990-х годов, после появления Java, автоматическое управление памятью стало общей практикой.
Существует множество реализаций JVM, как коммерческих, так и с открытым кодом. Одна из целей создания новых JVM — увеличение производительности для конкретной платформы. Каждая JVM пишется под платформу отдельно, при этом есть возможность написать ее так, чтобы она работала быстрее на конкретной платформе. Самая распространённая реализация JVM — это JVM Hotspot от OpenJDK. Также есть реализации IBM J9, Excelsior JET.
2. Выполнение кода на JVM
Согласно спецификации Java SE, для того, чтобы получить код, работающий в JVM, необходимо выполнить 3 этапа:
3. Загрузчики классов и их иерархия
Вернемся к загрузчикам классов — это специальные классы, которые являются частью JVM. Они загружают классы в память и делают их доступными для выполнения. Загрузчики работают со всеми классами: и с нашими, и с теми, которые непосредственно нужны для Java.
Представьте ситуацию: мы написали свое приложение, и помимо стандартных классов там есть наши классы, и их очень много. Как с этим будет работать JVM? В Java реализована отложенная загрузка классов, иными словами lazy loading. Это значит, что загрузка классов не будет выполняться до тех пор, пока в приложении не встретится обращение к классу.
Иерархия загрузчиков классов
Первый загрузчик классов — это Bootstrap classloader. Он написан на C++. Это базовый загрузчик, который загружает все системные классы из архива rt.jar. При этом, есть небольшое отличие между загрузкой классов из rt.jar и наших классов: когда JVM загружает классы из rt.jar, она не выполняет все этапы проверки, которые выполняются при загрузке любого другого класс-файла т.к. JVM изначально известно, что все эти классы уже проверены. Поэтому, включать в этот архив какие-либо свои файлы не стоит.
Следующий загрузчик — это Extension classloader. Он загружает классы расширений из папки jre/lib/ext. Допустим, вы хотите, чтобы какой-то класс загружался каждый раз при старте Java машины. Для этого вы можете скопировать исходный файл класса в эту папку, и он будет автоматически загружаться.
Еще один загрузчик — System classloader. Он загружает классы из classpath’а, который мы указали при запуске приложения.
Процесс загрузки классов происходит по иерархии:
4. Структура Сlass-файлов и процесс загрузки
Перейдем непосредственно к структуре Class-файлов.
Все числа, строки, указатели на классы, поля и методы хранятся в Сonstant pool — области памяти Meta space. Описание класса хранится там же и содержит имя, модификаторы, супер-класс, супер-интерфейсы, поля, методы и атрибуты. Атрибуты, в свою очередь, могут содержать любую дополнительную информацию.
Таким образом, при загрузке классов:
5. Исполнение байт-кода на JVM
В первую очередь, для исполнения байт-кода, JVM может его интерпретировать. Интерпретация — довольно медленный процесс. В процессе интерпретации, интерпретатор “бежит” построчно по класс-файлу и переводит его в команды, которые понятны JVM.
Также JVM может его транслировать, т.е. скомпилировать в машинный код, который будет исполняться непосредственно на CPU.
Команды, которые исполняются часто, не будут интерпретироваться, а сразу будут транслироваться.
6. Компиляция
Компилятор — это программа, которая преобразует исходные части программ, написанные на языке программирования высокого уровня, в программу на машинном языке, “понятную” компьютеру.
Компиляторы делятся на:
Также компиляторы могут классифицироваться по моменту компиляции:
7. Организация памяти в Java
Стек — это область памяти в Java, которая работает по схеме LIFO — “Last in — Fisrt Out” или “Последним вошел, первым вышел”.
Он нужен для того, чтобы хранить методы. Переменные в стеке существуют до тех пор, пока выполняется метод в котором они были созданы.
Когда вызывается любой метод в Java, создается фрейм или область памяти в стеке, и метод кладется на его вершину. Когда метод завершает выполнение, он удаляется из памяти, тем самым освобождая память для следующих методов. Если память стека будет заполнена, Java бросит исключение java.lang.StackOverFlowError. К примеру, это может произойти, если у нас будет рекурсивная функция, которая будет вызывать сама себя и памяти в стеке не будет хватать.
Ключевые особенности стека:
Куча разбита на несколько более мелких частей, называемых поколениями:
Почему отказались от Permanent generation? В первую очередь, это из-за ошибки, которая была связана с переполнением области: так как Perm имел константный размер и не мог расширяться динамически, рано или поздно память заканчивалась, кидалась ошибка, и приложение падало.
Meta space же имеет динамический размер, и во время исполнения он может расширяться до размеров памяти JVM.
Ключевые особенности кучи:
Основываясь на информации выше, рассмотрим, как происходит управление памятью на простом примере:
У нас есть класс App, в котором единственный метод main состоит из:
— примитивной переменой id типа int со значением 23
— ссылочной переменной pName типа String со значением Jon
— ссылочной переменной p типа person
Как уже упоминалось, при вызове метода на вершине стека создаётся область памяти, в которой хранятся данные, необходимые этому методу для выполнения.
В нашем случае, это ссылка на класс person: сам объект хранится в куче, а в стеке хранится ссылка. Также в стек кладется ссылка на строку, а сама строка хранится в куче в String pool. Примитив хранится непосредственно в стеке.
Для вызова конструктора с параметрами Person (String) из метода main() в стеке, поверх предыдущего вызова main() создается в стеке отдельный фрейм, который хранит:
— this — ссылка на текущий объект
— примитивное значение id
— ссылочную переменную personName, которая указывает на строку в String Pool.
После того, как мы вызвали конструктор, вызывается setPersonName(), после чего снова создается новый фрейм в стеке, где хранятся те же данные: ссылка на объект, ссылка на строку, значение переменной.
Таким образом, когда выполнится метод setter, фрейм пропадет, стек очистится. Далее выполняется конструктор, очищается фрейм, который был создан под конструктор, после чего метод main() завершает свою работу и тоже удаляется из стека.
Если будут вызваны другие методы, для них будут также созданы новые фреймы с контекстом этих конкретных методов.
8. Garbage collector
В куче работает Garbage collector — программа, работающая на виртуальной машине Java, которая избавляется от объектов, к которым невозможно получить доступ.
Разные JVM могут иметь различные алгоритмы сборки мусора, также существуют разные сборщики мусора.
Мы поговорим о самом простом сборщике Serial GC. Сборку мусора мы запрашиваем при помощи System.gc().
Как уже было упомянуто выше, куча разбита на 2 области: New generation и Old generation.
New generation (младшее поколение) включает в себя 3 региона: Eden, Survivor 0 и Survivor 1.
Old generation включает в себя регион Tenured.
Что происходит, когда мы создаем в Java объект?
В первую очередь объект попадает в Eden. Если мы создали уже много объектов и в Eden уже нет места, срабатывает сборщик мусора и освобождает память. Это, так называемая, малая сборка мусора — на первом проходе он очищает область Eden и кладёт “выжившие” объекты в регион Survivor 0. Таким образом регион Eden полностью высвобождается.
Если произошло так, что область Eden снова была заполнена, garbage collector начинает работу с областью Eden и областью Survivor 0, которая занята на данный момент. После очищения выжившие объекты попадут в другой регион — Survivor 1, а два остальных останутся чистыми. При последующей сборке мусора в качестве региона назначения опять будет выбран Survivor 0. Именно поэтому важно, чтобы один из регионов Survivor всегда был пустым.
JVM следит за объектами, которые постоянно копируются и перемещаются из одного региона в другой. И для того, чтобы оптимизировать данный механизм, после определённого порога сборщик мусора перемещает такие объекты в регион Tenured.
Когда в Tenured места для новых объектов не хватает, происходит полная сборка мусора — Mark-Sweep-Compact.
Во время этого механизма определяется, какие объекты больше не используются, регион очищается от этих объектов, и область памяти Tenured дефрагментируется, т.е. последовательно заполняется нужными объектами.