Открыть Электронные книги
Категории
Открыть Аудиокниги
Категории
Открыть Журналы
Категории
Открыть Документы
Категории
• Вопрос 2
Протокол TCP. Классы Socket и Serversocket
Ответ:
TCP - это транспортный протокол передачи данных. Чтобы передать пакеты, ему нужно
заранее установить соединение с сетью. После передачи пакета, протокол будет требовать
отчета о передаче, успешно или не успешно. Контролирует нагруженность сети. Поэтому этот
протокол считается более безопасным, надежным.
• Сокет. Оно обозначает точку, через которую происходит соединение. Проще говоря,
сокет соединяет в сети две программы. Класс Socket реализует идею сокета. Через его
каналы ввода/вывода будут общаться клиент с сервером.
Объявляется этот класс на стороне клиента, а сервер воссоздаёт его, получая сигнал на
подключение. Так происходит общение в сети.
Socket(String имя_хоста, int порт) throws UnknownHostException, IOException
Socket(InetAddress IP-адрес, int порт) throws UnknownHostException
«имя_хоста» — подразумевает под собой определённый узел сети, ip-адрес. Если класс сокета
не смог преобразовать его в реальный, существующий, адрес, то сгенерируется исключение
UnknownHostException.
Порт — есть порт. Если в качестве номера порта будет указан 0, то система сама выделит
свободный порт. Также при потере соединения может произойти исключение IOException.
Следует отметить тип адреса во втором конструкторе — InetAddress. Он приходит на помощь,
например, когда нужно указать в качестве адреса доменное имя. Так же когда под доменом
подразумевается несколько ip-адресов, то с помощью InetAddress можно получить их массив.
Тем не менее с ip он работает тоже. Так же можно получить имя хоста, массив байт
составляющих ip адрес и т.д. Мы немного затронем его далее, но за полными сведениями
придется пройти к официальной документации.
При инициализации объекта типа Socket, клиент, которому тот принадлежит, объявляет в сети,
что хочет соединиться с сервером по определённому адресу и номеру порта. Ниже
представлены самые часто используемые методы класса Socket:
InetAddress getInetAddress() – возвращает объект содержащий данные о сокете. В
случае если сокет не подключен – null
int getPort() – возвращает порт по которому происходит соединение с сервером
int getLocalPort() – возвращает порт к которому привязан сокет. Дело в том, что
«общаться» клиент и сервер могут по одному порту, а порты, к которым они привязаны –
могут быть совершенно другие
boolean isConnected() – возвращает true, если соединение установлено
void connect(SocketAddress адрес) – указывает новое соединение
boolean isClosed() – возвращает true, если сокет закрыт
boolean isBound() - возвращает true, если сокет действительно привязан к адресу
Класс Socket реализует интерфейс AutoCloseable, поэтому его можно использовать в
конструкции try-with-resources. Тем не менее закрыть сокет также можно классическим
образом, с помощью close().
Метод Описание
публичная пустая отправка (пакет Он используется для отправки пакета UDP
DatagramPacket)броски IOException на указанный порт рядом с пакетом.
публичный синхронизированный void Метод receive ожидает получения пакета из
receive(DatagramPacket пакет) выбрасывает порта, указанного пакетом и возвращает
исключение IOException результат.
публичная пустота закрыть() Он закрывает гнездо датаграммы.
public int getLocalPort() Он возвращает номер порта на локальном
хосте, к которому этот сокет привязан.
публичные синхронизированные пустые Он установил SO_TIMEOUT с указанным
setSOTimeout (int)броски SocketException временем ожидания в миллисекунды.
public synchronized int getSOTimeout () Он извлекает настройку для SO_TIMEOUT.
броски SocketException Он возвращает 0, это означает, что опция
отключена(т. е. тайм-аут бесконечности).
public getLocalAddress() Он получает локальный адрес, на который
находится сокет связанный.
public InetAddress getInetAddress() Если сокет подключен, то адрес является
возвращенный. В противном случае
возвращается значение null.
public int getPort() Он возвращает номер порта, к которому
подключен сокет соединенный.
public boolean isConnected() Он возвращает true, если сокет подключен к
a сервер.
public boolean isBound() Он возвращает true, если сокет привязан к
адресу.
Конструктор Описание
DatagramSocket () выбрасывает исключение Он создает экземпляр сокета datagram и
SocketException связывает его с любой неиспользуемый
номер порта на локальном компьютере.
DatagramSocket (int port)выбрасывает Он создает сокет дейтаграммы и связывает
исключение SocketException его с указанный порт на локальном хост-
компьютере.
DatagramSocket (int port, Он создает сокет дейтаграммы, привязанный
int,InetAddress)броски SockeEexception к указанному порт и InetAddress.
DatagramSocket (адрес SocketAddress)броски Он строит DatagramSocket, привязанный к
SocketException указанному SocketAddress.
• Вопрос 4
Отличия блокирующего и неблокирующего ввода-ввывода, их приемущества и
недостатки. Работа с сетевыми каналами
Ответ:
IO NIO
Потокоориентированный Буфер-ориентированный
Блокирующий (синхронный) ввод/вывод Неблокирующий (асинхронный)
ввод/вывод
Селекторы
Потоки ввода/вывода (streams) в Java IO являются блокирующими. Это значит, что когда в
потоке выполнения (tread) вызывается read() или write() метод любого класса из пакета
java.io.*, происходит блокировка до тех пор, пока данные не будут считаны или записаны.
Поток выполнения в данный момент не может делать ничего другого.
Таким образом неблокирующий режим Java NIO позволяет использовать один поток
выполнения для решения нескольких задач вместо пустого прожигания времени на ожидание в
заблокированном состояний. Наиболее частой практикой является использование
сэкономленного времени работы потока выполнения на обслуживание операций ввода/вывода
в другом или других каналах.
Как следует из названия, канал используется как среднее значение потока данных от
одного конца к другому. Здесь в канале Java NIO действуют одинаково между буфером
и объектом на другом конце, другими словами, канал используется для чтения данных
в буфер, а также для записи данных из буфера.
В отличие от потоков, которые используются в традиционных каналах Java IO, они
являются двухсторонними, т.е. могут считывать и записывать. Канал Java NIO
поддерживает асинхронный поток данных как в режиме блокировки, так и в режиме
без блокировки.
• Вопрос 5
Классы SocketChannel и DatagramChannel
Ответ:
Если вы не хотите работать с буферами, можете использовать для чтения из SocketChannel объект
Scanner.
• Вопрос 6
Передача данных по сети. Сериализация Объектов
Ответ:
«имя_хоста» — подразумевает под собой определённый узел сети, ip-адрес. Если класс
сокета не смог преобразовать его в реальный, существующий, адрес, то сгенерируется
исключение UnknownHostException.
Порт — есть порт. Если в качестве номера порта будет указан 0, то система сама выделит
свободный порт. Также при потере соединения может произойти исключение
IOException.
Следует отметить тип адреса во втором конструкторе — InetAddress. Он приходит на
помощь, например, когда нужно указать в качестве адреса доменное имя. Так же когда
под доменом подразумевается несколько ip-адресов, то с помощью InetAddress можно
получить их массив. Тем не менее с ip он работает тоже. Так же можно получить имя
хоста, массив байт составляющих ip адрес и т.д.
Класс Socket реализует интерфейс AutoCloseable, поэтому его можно использовать в
конструкции try-with-resources. Тем не менее закрыть сокет также можно классическим
образом, с помощью close().
• Сериализация объекта представляет процесс перевода какой-либо структуры
данных в последовательность битов. Обратной к операции сериализации
является операция десериализации, т.е. восстановление начального состояния
структуры данных из битовой последовательности. Существует два способа
сериализации объекта: стандартная сериализация java.io.Serializable и
«расширенная» сериализация java.io.Externalizable.
• Дополнительным бонусом ко всему является сохранение
кроссплатформенности. Не важно какая у вас операционная система,
сериализация переводит объект в поток байтов, который может быть
восстановлен на любой ОС. Если вам необходимо передать объект по сети, вы
можете сериализовать объект, сохранить его в файл и передать по сети
получателю. Он сможет восстановить полученный объект. Так же сериализация
позволяет осуществлять удаленный вызов методов (Java RMI), которые
находятся на разных машинах с, возможно, разными операционными
системами, и работать с ними так, словно они находятся на машине
вызывающего java-процесса.
• Вопрос 7
Интерфейс Serializable. Объектный граф, сериализация и десериализация полей и
методов
Ответ:
Интерфейс java.io.Serializable
При использовании Serializable применяется стандартный алгоритм сериализации,
который с помощью рефлексии (Reflection API) выполняет
• запись в поток метаданных о классе, ассоциированном с объектом (имя класса,
идентификатор SerialVersionUID, идентификаторы полей класса),
• рекурсивную запись в поток описания суперклассов до класса java.lang.Object
(не включительно),
• запись примитивных значений полей сериализуемого экземпляра, начиная с
полей самого верхнего суперкласса,
• рекурсивную запись объектов, которые являются полями сериализуемого
объекта.
При этом ранее сериализованные объекты повторно не сериализуются, что позволяет
алгоритму корректно работать с циклическими ссылками.
Для выполнения десериализации под объект выделяется память, после чего его поля
заполняются значениями из потока. Конструктор объекта при этом не вызывается.
Однако при десериализации будет вызван конструктор без параметров родительского
несериализуемого класса, а его отсутствие повлечёт ошибку десериализации.
• Реализовать механизм сериализации довольно просто. Необходимо, чтобы ваш
класс реализовывал интерфейс Serializable. Это интерфейс — идентификатор,
который не имеет методов, но он указывает jvm, что объекты этого класса
могут быть сериализованы. Так как механизм сериализации связан с базовой
системой ввода/вывода и переводит объект в поток байтов, для его
выполнения необходимо создать выходной поток OutputStream, упаковать его в
ObjectOutputStream и вызвать метод writeObject(). Для восстановления объекта
нужно упаковать InputStream в ObjectInputStream и вызвать метод
readObject().
В процессе сериализации вместе с сериализуемым объектом сохраняется его
граф объектов. Т.е. все связанные с этим объектом, объекты других классов
так же будут сериализованы вместе с ним.
Идентификатор версии есть у любого класса, который имплементирует интерфейс
Serializable. Он вычисляется по содержимому класса — полям, порядку объявления,
методам. И если мы поменяем в нашем классе тип поля и/или количество полей,
идентификатор версии моментально изменится. serialVersionUID тоже
записывается при сериализации класса. Когда мы пытаемся провести
десериализацию, то есть восстановить объект из набора байт, значение
serialVersionUID сравнивается со значением serialVersionUID класса в нашей
программе. Если значения не совпадают, будет выброшено исключение
java.io.InvalidClassException. Мы увидим пример этого ниже. Чтобы избежать таких
ситуаций, мы просто вручную задаем для нашего класса этот идентификатор
версии. В нашем случае он будет равен просто 1 (можешь подставить любое другое
понравившееся число).
Интерфейс java.io.Externalizable
При реализации интерфейса Externalizable вызывается пользовательская логика
сериализации. Способ сериализации и десериализации описывается в методах
writeExternal и readExternal. Во время десериализации вызывается конструктор без
параметров, а потом уже на созданном объекте вызывается метод readExternal.
Расширенный алгоритм сериализации может быть использован, если данные содержат
«конфиденциальную» информацию. В этом случае имеет смысл шифровать
сериализуемые данные и дешифровать их при десерилизации, что требует реализацию
собственного алгоритма.
• Для интерфейса Externalizable будет вызван метод получения конструктора
getExternalizableConstructor(), внутри которого мы через Reflection попробуем
получить конструктор по умолчанию класса, для которого мы восстанавливаем
объект. Если нам не удается его найти, или он не public, то мы получаем
исключение. Обойти эту ситуацию можно следующим образом: не создавать
явно никакого конструктора в классе и заполнять поля с помощью сеттеров и
получать значение геттерами. Тогда при компиляции класса будет создан
конструктор по умолчанию, который будет доступен для
getExternalizableConstructor(). Для Serializable метод getSerializableConstructor()
получает конструктор класса Object и от него ищет нужный класс, если не
найдет, то получим исключение ClassNotFoundException. Выходит, что
ключевое различие между Serializable и Externalizable в том, что первому не
нужен конструктор для создания восстановления объекта. Он просто полностью
восстановится из байтов. Для второго при восстановлении сначала будет создан
объект с помощью конструктора в точке объявления, а затем в него будут
записаны значения его полей из байтов, полученных при сериализации. Лично
мне больше нравится первый способ, он гораздо проще. Причем, даже если нам
нужно все таки задать поведение сериализации, мы можем не использовать
Externalizable, а так же реализовать Serializable, добавив (не переопределив) в
него методы writeObject() и readObject(). Но для того, чтобы они «работали»
нужно точно соблюсти их сигнатуру.
• Граф объектов — это набор объектов, которые будут сериализованы
автоматически, если объект, содержащий ссылку на них, сериализован.
Другими словами, мы можем сказать, что, когда мы сериализуем какой-либо
объект и если он содержит какую-либо другую ссылку на объект, JVM
сериализует объект, а также ссылки на его объекты.
Модификатор поля transient
Использование при описании поля класса модификатора transient позволяет
исключить указанное поле из сериализации. Это бывает полезно для секретных
(пароль) или не особо важных данных. Если, например, при описании объекта Person
включить следующее поле address
transient public String address;
то в результате сериализации и десериализации адрес объекта принимает значение по
умолчанию или будет null.
Модификатор transient действует только на стандартный механизм сериализации
Serializable. При использовании Externalizable никто не мешает сериализовать это поле,
равно как и использовать его для определения других полей.
Модификатор поля static
При стандартной сериализации поля, имеющие модификатор static, не сериализуются.
Соответственно, после десериализации это поле значения не меняет. При
использовании реализации Externalizable сериализовать и десериализовать статическое
поле можно, но не рекомендуется этого делать, т.к. это может сопровождаться
трудноуловимыми ошибками.
Модификатор поля final
Поля с модификатором final сериализуются как и обычные. За одним исключением –
их невозможно десериализовать при использовании Externalizable, поскольку final-
поля должны быть инициализированы в конструкторе, а после этого в readExternal
изменить значение этого поля будет невозможно. Соответственно, если необходимо
сериализовать объект с final-полем неоходимо использовать только стандартную
сериализацию.
Класс ObjectOutputStream
Для сериализации объектов в поток используется класс ObjectOutputStream. Он
записывает данные в поток.
Для создания объекта ObjectOutputStream в конструктор передается поток, в который
производится запись:
1 ObjectOutputStream(OutputStream out)
Для записи данных ObjectOutputStream использует ряд методов, среди которых можно
выделить следующие:
Десериализация. Класс ObjectInputStream
Класс ObjectInputStream отвечает за обратный процесс - чтение ранее сериализованных
данных из потока. В конструкторе он принимает ссылку на поток ввода:
1 ObjectInputStream(InputStream in)
Функционал ObjectInputStream сосредоточен в методах, предназначенных для чтения
различных типов данных. Рассмотрим основные методы этого класса:
• Вопрос 8
Java Stream API. Создание конвейеров. Промежуточные и терминальные операции.
Ответ:
Как говорилось выше, Stream API позволяет сократить количество строк кода.
Пример без потока:
Ещё одно отличие в сравнении с map, можно преобразовать один элемент в ноль, один
или множество других.
Для того, чтобы один элемент преобразовать в ноль элементов, нужно вернуть null,
либо пустой стрим. Чтобы преобразовать в один элемент, нужно вернуть стрим из
одного элемента, например, через Stream.of(x). Для возвращения нескольких
элементов, можно любыми способами создать стрим с этими элементами.
Тот же метод flatMap, но для Double, Integer и Long:
• flatMapToDouble(Function mapper)
• flatMapToInt(Function mapper)
• flatMapToLong(Function mapper)
IntStream.range(0,x) – выдаёт на поток элементов с 0 (включительно) по x (не
включительно);
limit(long maxSize) – ограничивает стрим по количеству элементов
skip(long n) – пропускаем n элементов
sorted()
sorted(Comparator comparator) – сортирует стрим (сортировка как у TreeMap)
distinct() — проверяет стрим на уникальность элементов(убирает повторы элементов);
dropWhile(Predicate predicate) — пропускает элементы которые удовлетворяют
условию (появился в 9 java, Функциональный интерфейс Predicate<T> проверяет
соблюдение некоторого условия. Если оно соблюдается, то возвращается значение true.
В качестве параметра лямбда-выражение принимает объект типа
Терминальные операторы:
forEach(Consumer action) – аналог for each (Consumer<T> выполняет некоторое
действие над объектом типа T, при этом ничего не возвращая);
count() – возвращает количество елементов стрима:
System.out.println(stream.count());
collect(Collector collector) – метод собирает все элементы в список, множество или
другую коллекцию, сгруппировывает элементы по какому-нибудь критерию,
объединяет всё в строку и т.д.:
List<String> list = Stream.of(“One”, “Two”, “Three”).collect(Collectors.toList());
collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner) — тот же, что и
collect(collector), только параметры разбиты для удобства (supplier поставляет новые
объекты (контейнеры), например new ArrayList(), accumulator добавляет элемент в
контейнер, combiner объединяет части стрима воедино);
reduce(T identity, BinaryOperator accumulator) — преобразовывает все элементы стрима
в один объект(посчитать сумму всех элементов, либо найти минимальный элемент),
cперва берётся объект identity и первый элемент стрима, применяется функция
accumulator и identity становится её результатом. Затем всё продолжается для
остальных элементов.
reduce(BinaryOperator accumulator) — такой же метод как и выше но отсутствует
начальный identity, им служит первый элемент стрима
Optional min(Comparator comparator)
Optional max(Comparator comparator) ищет минимальный/максимальный элемент,
основываясь на переданном компараторе;
findFirst() – вытаскивает первый элемент стрима
allMatch(Predicate predicate) — возвращает true, если все элементы стрима
удовлетворяют условию. Если встречается какой-либо элемент, для которого результат
вызова функции-предиката будет false, то оператор перестаёт просматривать элементы
и возвращает false
anyMatch(Predicate predicate) — вернет true, если хотя бы один элемент стрима
удовлетворяет условию predicate
некоторые методы Collectors:
toList() — собирает элементы в List
toSet() — cобирает элементы в множество
counting() — Подсчитывает количество элементов
joining()
joining(CharSequence delimiter)
joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix) — cобирает
элементы в одну строку. Дополнительно можно указать разделитель, а также префикс
и суффикс для всей последовательности
summingInt(ToIntFunction mapper)
summingLong(ToLongFunction mapper)
summingDouble(ToDoubleFunction mapper) — коллектор, который преобразовывает
объекты в int/long/double и подсчитывает сумму.
• Вопрос 9
Шаблоны проектирования. Decorator, Iterator, Factory method, Command, Flyweight,
Interpreter, Singleton, Strategy, Adapter, Facade, Proxy.
Ответ:
Паттерны бывают разные, т.к. решают разные проблемы. Обычно выделяют следующие
категории:
• Порождающие
Эти паттерны решают проблемы обеспечения гибкости создания объектов
• Структурные
Эти паттерны решают проблемы эффективного построения связей между объектами
• Поведенческие
Эти паттерны решают проблемы эффективного взаимодействия между объектами