1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/yaoyunpeng-java_senior_development_engineer_interview_notes

Клонировать/Скачать
part2-java基础.txt 57 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Отправлено 08.06.2025 19:58 b8b6f40
0,高级功能
反射
多线程
输入输出
静态导入
可变参数
参数化类型
枚举
注解
1,六个原则和二十三个设计模式
单一职责原则:类应该只负责一个功能区域
开闭原则:对扩展开放,对修改关闭
依赖倒置原则:依赖于抽象,而不是具体实现
接口隔离原则:使用多个隔离的接口比使用一个接口更好
里氏替换原则:如果子类对象可以替换父类对象,那么子类应该可以替换父类
最少知识原则:实体应该尽可能少地与其他实体交互,以确保功能模块的相对独立性
创建型 (5):
单例模式,
工厂方法模式,
抽象工厂模式,
建造者模式,
原型模式
结构型 (7):
适配器模式,
装饰器模式,
代理模式,
外观模式,
桥接模式,
组合模式,
单例模式
行为型 (11):
策略模式,
模板方法模式,
观察者模式, Паттерн итератор,
Паттерн цепочка обязанностей,
Паттерн команда,
Паттерн мемо,
Паттерн состояние,
Паттерн посещение,
Паттерн посредник,
Паттерн интерпретатор
2,JVM
a,Модель памяти
Программный счётчик: каждый поток имеет свой собственный небольшой участок памяти, который используется как часть процесса выполнения программы JVM. Он хранит адрес следующей инструкции, которую будет выполнять поток (только для Java методов, для Native методов значение этого счётчика не определено).
Стек Java-виртуальной машины: каждый поток имеет свой собственный стек, который используется для хранения информации о стековых кадрах, таких как локальные переменные, стек операндов, динамическое связывание, выход из метода и т. д. Каждый вызов метода и его возврат соответствуют добавлению и удалению стековых кадров из стека Java-виртуальной машины (параметр -Xss позволяет задать максимальный размер стека для потока, что также определяет максимальную глубину вызова функций).
Стек Native-методов: используется для хранения служб Native-методов.
Память кучи Java: параметры -Xmx и -Xms задают максимальный и начальный размеры кучи соответственно. Все потоки используют общую кучу, управляемую сборщиком мусора. Объекты, созданные JVM, хранятся в куче, которая делится на новую и старую генерацию. Новая генерация включает области Eden (80%), From Survivor (10%) и To Survivor (10%).Такое разделение объясняется тем, что 98% объектов в новой генерации умирают сразу после создания, поэтому используется стратегия сборки мусора с копированием, которая перемещает выжившие объекты из Eden и одной из Survivor-областей в другую Survivor-область, а затем очищает Eden и использованную Survivor-область.
Площадь методов: все потоки используют общую площадь методов, которая включает загруженные классы, константы, статические переменные, скомпилированные JIT-коды и т. д. Вспомогательная зона hotspot реализована с использованием стационарной памяти, в то время как другие JVM не имеют такого понятия.
В Java 6 методическая область включает данные, за исключением JIT-скомпилированных кодов, которые хранятся в области CodeCache в native memory, все остальное хранится в стационарной памяти;
В Java 7 хранение символов из PermGen было перемещено в native memory, а статические переменные были перемещены из конца instanceKlass (внутри PermGen) в конец объекта java.lang.Class (в обычной Java heap);
В Java 8 стационарная память была полностью удалена, и на её место пришла другая область native memory — метасpace (Metaspace), параметр -XX:MaxPermSize утратил смысл, и его заменил параметр -XX:MaxMetaspaceSize.
Метасpace: в JDK 1.8 стационарная память была удалена, и на её место пришла метасpace, обе они являются реализациями методической области JVM, но метасpace не находится в виртуальной машине, а хранится в native memory. Причины использования метасpace вместо стационарной памяти:
#строки хранятся в стационарной памяти, что может привести к проблемам производительности и выходу за пределы памяти
#размер информации о классах и методах трудно определить, поэтому указание размера стационарной памяти затруднено, слишком малый размер может привести к выходу за пределы стационарной памяти, слишком большой — к выходу за пределы старой памяти
#стационарная память усложняет работу сборщика мусора и снижает его эффективность
#Oracle может объединить HotSpot и JRockit
Правила распределения памяти:
#объекты предпочитают распределяться в Eden, когда недостаточно места для запуска Minor GC
#большие объекты сразу распределяются в старую память, длинные строки и массивы являются большими объектами
#объекты, которые долго живут, переходят в старую память, в новой памяти возраст объекта увеличивается на один после каждой Minor GC, когда возраст достигает 15 (по умолчанию), объект переходит в старую память, этот параметр можно настроить
Примечание: прямая память: прямая память не является частью областей данных JVM, но она также часто используется: в JDK 1. 4 введённая NIO предоставляет методы ввода-вывода на основе каналов и буферов, которые могут использовать native функции для прямого выделения памяти вне стека (java. nio. ByteBuffer).allocateDirect()), а затем использовать объект DirectByteBuffer как ссылку на эту память для её использования (подробнее см. Java I/O расширение),
таким образом избегается копирование данных между стеком Java и native памятью, что может значительно повысить производительность в определённых сценариях.
Очевидно, что прямое выделение памяти не подчиняется ограничениям размера стека Java (т. е. не следует ограничениям -Xms, -Xmx и т. д.), но поскольку это память, то она всё равно будет ограничена общей памятью компьютера и адресуемым пространством процессора, поэтому при динамическом расширении также могут возникать исключения OutOfMemoryError.
b, механизмы сборки мусора
#алгоритмы определения смерти объектов #Метод подсчёта ссылок: добавить счетчик ссылок к объекту, увеличивать его на 1 каждый раз, когда объект ссылается на него, и уменьшать на 1, когда ссылка становится недействительной.
#Когда счётчик достигает 0, это указывает на то, что объект больше не используется.
#Этот метод прост и эффективен, но у него есть проблема циклических взаимных ссылок между объектами.
#Алгоритм анализа достижимости: это основной алгоритм, используемый большинством языков для определения выживаемости объектов.
#Принцип заключается в том, что если объект недоступен от GC Roots, он считается кандидатом для сборки мусора.
##В языке Java объекты, которые могут быть рассмотрены как GC Roots, включают: объекты, ссылки на которые находятся в стеке виртуальной машины, объекты, ссылки на которые находятся в статических атрибутах кучи Java, объекты, ссылки на которые находятся в константах области методов, и объекты, ссылки на которые находятся в стеке методов JNI.
#Алгоритмы сборки мусора
#Алгоритм маркировки и очистки: самый базовый алгоритм сборки мусора, последующие алгоритмы основаны на этой идее.
#Он состоит из двух процессов: маркировки и очистки.
#Однако он не очень эффективен и создаёт большое количество непрерывных фрагментов памяти.When allocating large objects, it may not be able to find enough contiguous memory, leading to an early garbage collection.
#Copying algorithm: simple and efficient, currently used by commercial virtual machines to collect the young generation. It copies the surviving objects from Eden and one of the Survivor spaces to the other Survivor space, then clears Eden and the used Survivor space.
#Mark-compact algorithm: when the object survival rate is high, the efficiency of the copying algorithm decreases. Therefore, based on the characteristics of the old generation, this algorithm is designed to move all surviving objects to one end, and then directly clear the memory outside the end boundary.
#Generational collection: based on the different lifespans of objects, choose one of the above three algorithms for collection. The young generation in the Java heap uses the copying algorithm, while the old generation uses the mark-compact algorithm.
#Garbage collectors
#Serial garbage collector: the most basic and oldest collector, it uses a single thread for garbage collection, freezing all application threads to work. It may be used in client environments.
#Parallel garbage collector: uses multiple threads for garbage collection, stopping all other application threads.
#Concurrent Mark-Sweep garbage collector CMS: based on the mark-and-sweep algorithm, it mainly consists of four steps: initial marking - concurrent marking - remarking - concurrent sweeping. Initial marking and remarking still require stopping other application threads. Concurrent collection, low pause. Disadvantage: uses the mark-and-sweep algorithm.
#G1 garbage collector: the most advanced collector currently, replacing CMS. It divides the entire Java heap into multiple equal-sized regions. Features: parallel and concurrent, generational collection, space consolidation (mark-compact), predictable pause
c, class loading
#Class loading process in 5 steps
#Loading
#Obtain the binary byte stream that defines the class through its fully qualified name
#Convert the static storage represented by the byte stream into a runtime data structure in the method area
# Создание объекта класса в памяти, который будет служить входом для доступа к различным данным этого класса в области методов
## Валидация
# Валидация формата файла: проверка соответствия байтового потока стандартам формата Class
# Валидация метаданных: наличие родительского класса, наследование родительского класса, неабстрактный класс, реализация родительского класса или интерфейса
# Валидация байт-кода: проверка корректности семантики и логики
# Валидация ссылок: проверка доступности ссылок на классы, поля и методы
## Подготовка
# Выделение памяти для статических переменных и установка начальных значений
## Разбор
# Разбор класса или интерфейса
# Разбор полей
# Разбор методов класса
# Разбор методов интерфейса
## Инициализация
# Выполнение метода <clinit>(): вызов статических блоков кода и присвоение статическим переменным значений, начиная с родительского класса
# Загрузчики классов
# Четыре типа загрузчиков классов: загрузчик классов платформы (реализован на C++), расширяемый загрузчик классов -> приложение загрузчик классов -> пользовательский загрузчик классов
# Модель двойного делегирования: при получении запроса на загрузку класса загрузчик классов сначала проверяет, загружен ли этот класс, если нет, то передает запрос родительскому загрузчику классов ## Родительский загрузчик классов также проверяет, загружен ли класс. Если нет, то передает запрос еще выше, до тех пор, пока родительский загрузчик классов не скажет, что этот класс не должен быть загружен им. Тогда дочерний загрузчик классов загружает класс. Преимущества: безопасность, предотвращение повторной загрузки.
# Внимание: многие статьи описывают иерархию загрузчиков классов, помещая загрузчик классов платформы на уровень выше расширяемого загрузчика классов, что неверно, так как загрузчик классов платформы реализован на C++ и является частью JVM, а не иерархии загрузчиков классов JVM.
## Кроме того, у загрузчика классов платформы нет дочерних загрузчиков классов.
3. Фреймворк коллекций
# Обычные структуры данных:
# Стек (Stack): последний входит, первый выходит, операции добавления и удаления элементов выполняются только на вершине стека.
# Очередь (Queue): первый входит, первый выходит, элементы добавляются только в конец очереди, а удаляются только из начала.
# Массив (Array): последовательное выделение памяти, быстрый доступ к элементам, но медленное добавление и удаление элементов.
# Связный список (Linked): несвязное выделение памяти, каждый узел содержит информацию о следующем и предыдущем узле, поэтому доступ к элементам медленный, но добавление и удаление элементов быстрые. # Хэш-таблица (Hash): комбинация преимуществ массива и связного списка, состоит из массива и связного списка
# Двоичное дерево (Tree): каждая вершина имеет не более двух поддеревьев, компромисс между массивом и связным списком
# Коллекция (Collection)
# Список (List): позволяет дублировать элементы
# ArrayList: быстрый поиск, медленное добавление и удаление, реализован с помощью массива, по умолчанию размер 10, расширяется на 50% + 1 элемент
# LinkedList: быстрое добавление и удаление, медленный поиск, реализован с помощью двусвязного списка
# Vector: похож на ArrayList, реализован с помощью массива, по умолчанию размер 10, потокобезопасен (synchronized), но менее эффективен, не рекомендуется для использования, даже если требуется потокобезопасность. Расширяется в 2 раза.
# Stack: последний входит, первый выходит, наследуется от Vector, реализован с помощью массива, потокобезопасен, но менее эффективен.
# Множество (Set): не позволяет дублирование
# HashSet: неупорядоченный, элементы хранятся по алгоритму хеширования, основан на HashMap, ключи хранятся в качестве элементов, значение - это финальный объект, имеет хорошую производительность для хранения и поиска, не потокобезопасен
# TreeSet: элементы хранятся с помощью структуры красно-черного дерева, основан на TreeMap, значение - это финальный объект, TreeSet поддерживает два метода сортировки: естественную и пользовательскую. #LinkedHashSet: подкласс HashSet, использует связный список для поддержания порядка элементов
#Заключение
#HashSet имеет наивысшую производительность, для реализации отсортированного Set следует использовать TreeSet, для сохранения порядка вставки - LinkedHashSet
#В HashSet и TreeSet следует добавлять только неизменяемые объекты
#Все три реализации Set не потокобезопасны. Если несколько потоков одновременно обращаются к Set, необходимо вручную обеспечить потокобезопасность. Например, с помощью метода synchronizeSorted из класса Collections
#Map
#HashMap: HashMap в глубине использует Entry объект для хранения key-value пар. Внутри HashMap используется массив Entry[], который хранит все key-value пары. При добавлении Entry объекта используется хеш-алгоритм для определения его позиции в массиве, а метод equals для определения его позиции в связанном списке на этой позиции. При извлечении Entry объекта используется хеш-алгоритм для нахождения его позиции в массиве, а метод equals для извлечения Entry из связанного списка на этой позиции.
##По умолчанию размер 16, коэффициент загрузки 0.75, можно использовать Collections.synchronizeMap(hashMap) для обеспечения потокобезопасности
#LinkedHashMap: похож на HashMap, но сохраняет порядок вставки элементов, что полезно при сортировке #Hashtable: имеет ту же структуру данных, что и HashMap, но потокобезопасен (синхронизирован), менее эффективен, рекомендуется использовать ConcurrentHashMap для обеспечения потокобезопасности
#ConcurrentHashMap: с версии JDK 1.5 потокобезопасен, но при итерации ConcurrentHashMap блокирует только часть карты, в то время как Hashtable блокирует всю карту. #TreeMap: используемая базовая структура данных — бинарное дерево, неупорядоченное, не допускающее дублирование (неупорядоченность означает, что порядок элементов не совпадает с порядком добавления). TreeMap коллекция по умолчанию сортирует ключи, поэтому ключи должны реализовывать естественное или пользовательское сравнение.4. Конкуренция
# Основная память и рабочая память: в Java экземплярные поля, статические поля и элементы массивов объектов хранятся в основной памяти. Каждый поток, который должен выполнять операции с общими переменными, должен взаимодействовать с основной памятью.
# Взаимодействие памяти: 8 типов
# Закрытие lock: применяется к переменным основной памяти, оно помечает переменную как эксклюзивную для одного потока.
# Открытие unlock: применяется к переменным основной памяти, оно освобождает переменную, находящуюся в состоянии lock, освобожденная переменная может быть заблокирована другим потоком.
# Чтение read: применяется к переменным основной памяти, оно передает значение переменной из основной памяти в рабочую память потока для последующего использования в операции load.
# Загрузка load: применяется к переменным рабочей памяти, оно помещает значение переменной, полученное в результате операции read из основной памяти, в рабочую память.
# Использование use: применяется к переменным рабочей памяти, оно передает значение переменной из рабочей памяти в выполнение, каждый раз, когда виртуальная машина встречает инструкцию байт-кода, которая требует использования значения переменной, выполняется эта операция.#Присваивание assign: применяется к переменным рабочей памяти, оно присваивает значение, полученное из выполнения, переменной в рабочей памяти, каждый раз, когда виртуальная машина встречает инструкцию байт-кода, которая требует присвоения значения переменной, выполняется эта операция.
#Хранение store: применяется к переменным рабочей памяти, оно передает значение переменной из рабочей памяти в основную память для последующего использования в операции write.
#Запись write: применяется к переменным основной памяти, оно помещает значение переменной, полученное в результате операции store из рабочей памяти, в основную память.
#Чтение read и загрузка load, хранение store и запись write должны соответствовать один к одному.
#Три характеристики конкуренции
#Атомарность: базовые типы данных имеют атомарность при доступе и записи (для long и double, которые являются 64-битными типами, атомарность не гарантируется на 32-битных компьютерах), synchronized может гарантировать атомарность на более высоком уровне.
#Видимость: когда один поток изменяет общую переменную, другие потоки могут немедленно получить новое значение, volatile, synchronized и final могут гарантировать видимость.# Порядок выполнения: Java имеет естественный порядок выполнения: внутри потока операции выполняются последовательно, наблюдение одного потока за другим потоком показывает, что все операции неупорядочены: перестройка инструкций и задержка синхронизации рабочей памяти с основной памятью.
# Ключевое слово volatile
# Легковесная синхронизация, не сохраняет копии, каждый раз перед использованием обновляет значение из основной памяти. # Обеспечивает видимость, но не гарантирует атомарность, поэтому при многопоточной модификации может быть получен неверный результат
# Запрещает оптимизацию перестановки инструкций
# Сценарии использования: вычисления, не зависящие от текущего результата или наличие только одного потока, который изменяет значение переменной, и отсутствие необходимости в совместном участии других состоятельных переменных для обеспечения неизменности, например, в классе ThreadPoolExecutor, определены много переменных типа volatile для обеспечения видимости других переменных
# volatile также обеспечивает атомарность, например, чтение 64-битных данных, таких как long и double, не являются атомарными, но volatile типы данных double и long являются атомарными.# Поток
# Определение: поток является минимальной единицей планирования CPU (процесс является минимальной единицей распределения ресурсов CPU), поток является единицей выполнения программы, основанной на процессе, и все потоки могут совместно использовать ресурсы процесса.
# Три способа создания потока:
# Наследование класса Thread и переопределение метода run.
# Реализация интерфейса Runnable и переопределение метода run.
# Создание потока с помощью Callable и Future, процесс сложный, не часто используется.
# Сравнение трех способов:
# 1 и 3 реализуют интерфейсы, могут наследовать другие классы, несколько потоков могут совместно использовать один объект target, недостаток — использование метода Thread.currentThread() для доступа к текущему потоку.
# 2 использует наследование, не может наследовать другие классы, доступ к текущему потоку через this.
# Способ с Callable и Future может иметь возвращаемое значение, в то время как Runnable нет.
# Рекомендуется реализация интерфейса Runnable для создания потока.
# Потоки имеют 10 уровней приоритета.
# Пять состояний:
# Новый new: поток находится в этом состоянии после создания, но до запуска. start() -> running.
# Выполняющийся runnable: поток выполняется или ожидает распределения времени выполнения CPU. Thread.yield(): поток отказывается от выполнения, передавая управление CPU.
# Ожидание waiting:# Безлимитное ожидание: требуется явное пробуждение другими потоками notify()/notifyAll() -> Running -> wait()
# Метод Object. wait() без параметра Timeout
# Метод Thread. join() без параметра Timeout
# Метод LockSupport. park()
# Ограниченное ожидание: не требуется явное пробуждение другими потоками
# Метод Thread. sleep()
# Метод Object. wait() с параметром Timeout
# Метод Thread. join() с параметром Timeout
# Метод LockSupport. parkNanos()
# Метод LockSupport. parkUntil()
# Блокировка blocked: ожидание освобождения блокировки другим потоком, synchronized
# Завершение terminated: состояние завершенного потока, поток завершил выполнение. run() завершено
# Потокобезопасность
# Определение: Если объект может безопасно использоваться несколькими потоками одновременно, то он считается потокобезопасным.
# Реализация:
- **Блокирующая синхронизация:** `synchronized` и повторно-захватываемый блок (`ReentrantLock`).
- **Неблокирующая синхронизация:** сначала выполняется операция, если нет конкурирующих потоков, операция успешно завершается; если возникает конфликт, применяются дополнительные меры.
- **Отсутствие синхронизации:** некоторые операции по своей природе безопасны, например, если они не используют общие данные.
# Пул потоков
- **Определение:** Частое создание и уничтожение потоков потребляет много ресурсов.Отводится область памяти, содержащая множество (не уничтоженных) потоков, управление и распределение которых осуществляется менеджером пула.
- **Реализация:** `java.util.concurrent.ThreadPoolExecutor`
- **Преимущества:**
- **Устранение затрат на создание и уничтожение потоков.**
- **Устранение блокировок, вызванных конкуренцией за ресурсы.**
- **Упрощенное управление потоками и предоставление функций, таких как планирование задач на определенное время или периодическое выполнение.**
# Живые мертвецы (Deadlock)
- **Определение:** Когда несколько потоков одновременно ожидают, чтобы другие потоки освободили блокировку, что приводит к бесконечной блокировке.
- **Причины:** Поток A захватывает блокировку 1, и блокировка 1 переходит в состояние блокировки, другие потоки, желающие получить эту блокировку, должны ждать. Поток B захватывает блокировку 2, и блокировка 2 переходит в состояние блокировки. Теперь, когда поток A пытается захватить блокировку 2, а поток B пытается захватить блокировку 1, и оба потока не освобождают свои блокировки, поток A ждет, пока поток B не освободит блокировку 2, а поток B ждет, пока поток A не освободит блокировку 1. Это приводит к бесконечной блокировке, что и вызывает deadlock.
- **Методы предотвращения:**
- **Порядок захвата блокировок:** В приведенном выше примере deadlock возникает из-за различного порядка захвата блокировок потоками A и B.Если порядок захвата блокировок будет одинаковым, deadlock можно избежать.
- **Время ожидания захвата блокировок:** Установка времени ожидания для блокировки. Если поток не может захватить все необходимые блокировки в течение заданного времени, он откатывается, освобождает все захваченные блокировки и ждет случайное время перед повторной попыткой.
- **Обнаружение deadlock:** Обнаружение deadlock является лучшим методом предотвращения deadlock, особенно для ситуаций, где невозможно обеспечить последовательность захвата блокировок или установить время ожидания. В этом случае можно позволить потоку A проверять, не запросил ли поток B блокировку, которую держит поток A. Если поток B действительно делает запрос, поток A отменяет запрос, освобождает блокировку 1, откатывается и ожидает. Конечно, в реальности могут пересекаться несколько потоков, и ему нужно последовательно проверять.5. Пакет java.util.concurrent
# Рекомендуемые книги: "Java High Concurrency Programming" рекомендует этот пакет и описывает его использование.
# Описание: В Java 5 был добавлен новый пакет в Java платформу, java.util.concurrent. Этот пакет содержит набор классов, которые делают параллельное программирование на Java более простым и удобным. Перед добавлением этого пакета вам приходилось самостоятельно реализовывать свои инструменты.# Блокирующие очереди BlockingQueue:
# Очередь блокировки на основе массива ArrayBlockingQueue:
# Очередь с задержкой DelayQueue:
# Очередь блокировки на основе списка LinkedBlockingQueue:
# Синхронная очередь SynchronousQueue: внутренне она может содержать только один элемент. Если очередь уже содержит элемент, поток, пытаясь добавить новый элемент, будет заблокирован до тех пор, пока другой поток не извлечет существующий элемент из очереди.
# Блокирующая двусторонняя очередь BlockingDeque:
# Параллельная карта ConcurrentMap: ConcurrentHashMap очень похожа на java.util.HashTable, но ConcurrentHashMap обеспечивает лучшую производительность при параллельном использовании. При чтении объектов из нее не блокируется вся карта, а только часть, которая находится под воздействием. При записи объектов в нее также не блокируется вся карта, а только часть, которая находится под воздействием.
# Барьер CyclicBarrier: это синхронизационный механизм, который представляет собой барьер, через который должны пройти все потоки. Только после того, как все потоки достигнут этого барьера, они смогут продолжить выполнение других задач.
# Обменник Exchanger: представляет собой механизм, который позволяет двум потокам обмениваться объектами.
# Услуги исполнителя ExecutorService: это интерфейс, который позволяет реализовать пул потоков.
# Пул потоков ThreadPoolExecutor: реализует ExecutorService.# Услуги планировщика ScheduledExecutorService: позволяет задерживать выполнение задач или выполнять их с фиксированным интервалом. Задачи выполняются асинхронно рабочим потоком, а не потоком, который их отправил.
# Пул потоков для вилок и слияний ForkJoinPool: похож на ExecutorService, но позволяет легко разделять задачи на несколько меньших и объединять их.
# Блокировка Lock: это механизм синхронизации потоков, похожий на synchronized блоки. Однако Lock более гибок и детализирован по сравнению с блоками synchronized.
# Чтение-запись ReadWriteLock: чтение-запись — это продвинутая система блокировки потоков. Она позволяет нескольким потокам одновременно читать определенный ресурс, но одновременно может выполнять запись только один поток.
# Атомарный булев тип AtomicBoolean: предоставляет атомарное чтение и запись булевого значения.
# Атомарный целочисленный тип AtomicInteger
# Атомарный длинный целочисленный тип AtomicLong
# Атомарный ссылочный тип AtomicReference
6,io/nio(jdk1.4)
# Поток: поток — это последовательность байтов с началом и концом, это общее или абстрактное понятие для передачи данных. Суть — это передача данных, которая абстрактно представлена различными классами в зависимости от характеристик передачи данных, что позволяет более наглядно работать с данными.
# io
#Классификация: По типу данных разделены на: символьные потоки и байтовые потоки, по направлению потока — на входные потоки и выходные потоки.
#Байтовые потоки работают с байтами, а символьные потоки — с символами. Одновременно можно читать 2 байта. Байтовые потоки могут обрабатывать все типы данных, в то время как символьные потоки могут обрабатывать только символы. Для обработки чистого текста предпочтительнее использовать символьные потоки, а для всего остального — байтовые потоки.
#Байтовый входной поток InputStream:
#InputStream является родительским абстрактным классом для всех байтовых входных потоков.
#ByteArrayInputStream, StringBufferInputStream, FileInputStream — это три основных типа потоков, которые читают данные из массива байтов, StringBuffer и локального файла соответственно. PipedInputStream — это поток, который читает данные из общего для других потоков канала.
#ObjectInputStream и все подклассы FilterInputStream — это декораторы (главные акторы паттерна декоратора).
#Байтовый выходной поток OutputStream:
#OutputStream является родительским абстрактным классом для всех байтовых выходных потоков.
#ByteArrayOutputStream, FileOutputStream — это два основных типа потоков, которые записывают данные в массив байтов и локальный файл соответственно. PipedOutputStream — это поток, который записывает данные в общий для других потоков канал.
#ObjectOutputStream и все подклассы FilterOutputStream — это декораторы. #Символьный входной поток Reader:
#Reader является родительским абстрактным классом для всех символьных входных потоков.
#CharReader, StringReader — это два основных типа потоков, которые читают данные из массива символов и строки соответственно. PipedReader — это поток, который читает данные из общего для других потоков канала.
#BufferedReader — это декоратор, который и его подклассы используют для декорирования других объектов Reader. #FilterReader является родительским классом для всех пользовательских декораторов потоков, его подкласс PushbackReader декорирует объекты Reader, добавляя номер строки.
#InputStreamReader является мостом между потоками байтов и потоками символов, он преобразует поток байтов в поток символов. FileReader можно рассматривать как часто используемый инструментарий для этой функции, в его исходном коде явно используется преобразование FileInputStream в Reader. Мы можем извлечь определенные приемы из этого класса. Потоки Reader различных классов имеют схожие функции и методы использования, как и у потоков InputStream. В дальнейшем будут представлены соответствия между Reader и InputStream.#Символьный выводной поток Writer:
#Writer является родительским классом для всех символьных выводных потоков, он является абстрактным классом.
#CharArrayWriter и StringWriter представляют два базовых потока, они записывают данные в массив символов и строку соответственно. PipedWriter записывает данные в канал, используемый другими потоками.
#BufferedWriter является декоратором, который добавляет буфер к Writer.
#PrintWriter и PrintStream очень похожи по функциональности и использованию.
#OutputStreamWriter является мостом между OutputStream и Writer, его подкласс FileWriter является конкретной реализацией этой функции (можно изучить исходный код). Функциональность и использование очень похожи на OutputStream, в дальнейшем будут представлены их соответствия.#nio
#io использует одностороннюю передачу байтов и символов, управление блокировками I/O операций осуществляется на уровне потока, обслуживающего запрос, что может привести к тому, что множество потоков будут простаивать, ожидая, что приведет к низкой эффективности использования ресурсов I/O и снижению производительности всей системы.
#NIO, напротив, использует блоки, которые можно рассматривать как буферы, передавая их по частям, что обеспечивает более высокую скорость. Один поток NIO может одновременно передавать несколько блоков, что называется асинхронной передачей.
#Принципы неблокирующего режима: событийная модель: события срабатывают при их возникновении, а не при синхронном мониторинге.
#NIO инструментарий предлагает новую модель, основанную на буферах (Buffer), каналах (Channel) и селекторах (Selector).
#внутренняя структура буфера
#Основной буфер контролируется тремя переменными: position, limit, capacity, которые управляют процессом чтения и записи.
#параметры: режим записи; режим чтения
#position: текущее количество записанных единиц данных; текущее положение для чтения единиц данных.
#limit: максимальное количество единиц данных, которые можно записать, равно емкости; максимальное количество единиц данных, которые можно прочитать, равно предыдущему количеству записанных единиц данных.
#capacity: емкость буфера; емкость буфера
#общие методы:#flip(): переводит режим записи в режим чтения.
#rewind() : сбрасывает позицию обратно к 0, обычно используется для повторного чтения.
#clear() : очищает буфер, готовит его для повторного заполнения (позиция становится 0, limit становится capacity).
#compact(): копирует непрочитанные данные в начало буфера.
#mark() 、 reset(): mark может пометить позицию, reset может сбросить позицию к этой метке.
#Основные классы: ByteBuffer 、 MappedByteBuffer 、 CharBuffer 、 DoubleBuffer 、 FloatBuffer 、 IntBuffer 、 LongBuffer 、 ShortBuffer 。
#Основные каналы: FileChannel 、 DatagramChannel(UDP) 、 SocketChannel(TCP клиент) 、 ServerSocketChannel(TCP сервер)
#Selector: ключевой класс для асинхронного ввода-вывода, который может обнаруживать события на одном или нескольких каналах и распределять их. Использование одного select-потока позволяет прослушивать события на нескольких каналах и реагировать на них в зависимости от событий, без необходимости назначения отдельного потока для каждого канала.
#SelectionKey: содержит информацию о состоянии события и привязку к соответствующему каналу.
#Пример кода:
String infile = "C:\\copy. sql";
String outfile = "C:\\copy. txt";
// Получаем входной и выходной потоки для исходного и целевого файлов
FileInputStream fin = new FileInputStream(infile);
FileOutputStream fout = new FileOutputStream(outfile);
// Получаем входной и выходной каналы
FileChannel fcin = fin.getChannel();
FileChannel fcout = fout.getChannel();
// Создаем буфер
ByteBuffer buffer = ByteBuffer.allocate(1024);
while (true) { // clear метод сбрасывает буфер, чтобы он мог принимать данные для чтения
buffer.clear();
// Читаем данные из входного канала в буфер
int r = fcin.read(buffer);
// read метод возвращает количество прочитанных байтов, может быть нулевым, если канал достиг конца потока, возвращает -1
if (r == -1) {
break;
}
// Переключаемся из режима записи в режим чтения,
buffer.flip();
// Записываем данные из выходного канала в буфер
fcout.write(buffer);
7, аннотации
8, прокси
#статический прокси: компиляция для улучшения, изменение класса объекта
#AspectJ: он включает аспекты в Java байт-код на этапе компиляции, при выполнении это уже улучшенный AOP объект.
#динамический прокси: улучшение во время выполнения, создание прокси-объекта
#jdk динамический прокси: использует механизм рефлексии для создания анонимного класса, реализующего интерфейс прокси, вызывает InvocationHandler перед вызовом конкретного метода.
#объект, который подлежит проксированию, должен реализовывать интерфейс
#Создайте прокси-класс, реализующий интерфейс InvocationHandler
#Используйте Proxy.newProxyInstance(classLoader, interfaces, handler) для создания прокси-объекта
#динамическое прокси-создание с использованием CGLib: использует библиотеку asm (небольшой и легкий фреймворк для работы с байт-кодом), чтобы загрузить файл class прокси-объекта и изменить его байт-код для создания подкласса
#необходимо использовать библиотеку CGLib
#можно создать прокси для классов, не реализующих интерфейсы
#Enhancer является основным классом

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/yaoyunpeng-java_senior_development_engineer_interview_notes.git
git@api.gitlife.ru:oschina-mirror/yaoyunpeng-java_senior_development_engineer_interview_notes.git
oschina-mirror
yaoyunpeng-java_senior_development_engineer_interview_notes
yaoyunpeng-java_senior_development_engineer_interview_notes
master