ar
Feedback
Библиотека Java разработчика

Библиотека Java разработчика

الذهاب إلى القناة على Telegram

📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP

إظهار المزيد

📈 نظرة تحليلية على قناة تيليجرام Библиотека Java разработчика

تُعد قناة Библиотека Java разработчика (@bookjava) في القطاع اللغوي الروسية لاعباً نشطاً. يضم المجتمع حالياً 10 265 مشتركاً، محتلاً المرتبة 12 016 في فئة التكنولوجيات والتطبيقات والمرتبة 63 847 في منطقة روسيا.

📊 مؤشرات الجمهور والحراك

منذ تأسيسه في невідомо، حقق المشروع نمواً سريعاً وجمع 10 265 مشتركاً.

بحسب آخر البيانات بتاريخ 13 يونيو, 2026، تحافظ القناة على نشاط مستقر. خلال آخر 30 يوماً تغيّر عدد الأعضاء بمقدار 13، وفي آخر 24 ساعة بمقدار 3، مع بقاء الوصول العام مرتفعاً.

  • حالة التحقق: غير موثّقة
  • معدل التفاعل (ER): يبلغ متوسط تفاعل الجمهور 8.59‎%. وخلال أول 24 ساعة من النشر يحصد المحتوى عادةً 4.32‎% من ردود الفعل نسبةً إلى إجمالي المشتركين.
  • وصول المنشورات: يحصل كل منشور على متوسط 882 مشاهدة. وخلال اليوم الأول يجمع عادةً 443 مشاهدة.
  • التفاعلات والاستجابة: يتفاعل الجمهور بانتظام؛ متوسط التفاعلات لكل منشور يبلغ 7.
  • الاهتمامات الموضوعية: يركز المحتوى على مواضيع رئيسية مثل string, интерфейс, строка, boot, api.

📝 الوصف وسياسة المحتوى

يصف المؤلف القناة بأنها مساحة للتعبير عن الآراء الذاتية:
📚 Лайфхаки, приёмы и лучшие практики для Java-разработчиков. Всё, что ускорит код и прокачает навыки. Java, Spring, Maven, Hibernate. По всем вопросам @evgenycarter РКН clck.ru/3KoGeP

بفضل وتيرة التحديث المرتفعة (أحدث البيانات بتاريخ 14 يونيو, 2026) تحافظ القناة على حداثتها ومستوى وصول مرتفع. وتُظهر التحليلات تفاعلاً نشطاً من الجمهور، ما يجعلها نقطة تأثير مهمة ضمن فئة التكنولوجيات والتطبيقات.

10 265
المشتركون
+324 ساعات
-157 أيام
+1330 أيام
أرشيف المشاركات
Как получить гарантированный дедлок? Сначала поговорим о том, что это такое. Deadlock – это взаимная блокировка, ситуация, когда два или более потока «наступают друг-другу на хвост» – зависают в вечном ожидании ресурсов, захваченных друг другом. Livelock – похожая проблема, с тем лишь отличием, что потоки не останавливаются, а вместо этого зацикливаются, выполняя одни и те же бесполезные действия, ходят по кругу. Стандартный подход к обеспечению гарантии защиты от дедлока – установка строгого порядка взятия блокировок. Если для мониторов A и B соблюдается всеобщий порядок захвата AB (и соответственно отпускания BA), то ни с одним потоком не случится попасть на ожидание B, успешно при этом захватив A. Из этого можно догадаться, простой способ гарантировать возможность дедлока – явно нарушить это условие. Нарушение условия даст дедлок «скорее всего когда-нибудь». Чтобы получить его точно и с первого раза, нужно гарантировать, что оба потока окажутся на этапе между захватами одного и другого ресурса в одно время. Это можно сделать множеством способов, в примере ниже использован CyclicBarrier. Вопрос дедлоков – одна из краеугольных тем параллельных вычислений, уходящая далеко за рамки этого вопроса. Для дальнейшего изучения рекомендуются статьи на википедии про дедлоки, про задачу об обедающих философах как классическая иллюстрация проблемы, и глава 10.1 Java Concurrency in Practice. #Многопоточность

Spring в действии 6 изд Крейг Уоллс (2022) Фреймворк Spring Framework - необходимый инструмент для разработчиков приложений н
Spring в действии 6 изд Крейг Уоллс (2022) Фреймворк Spring Framework - необходимый инструмент для разработчиков приложений на Java. В книге описана последняя версия Spring 3, который несет в себе новые мощные особенности, такие как язык выражений SpEL, новые аннотации для работы с контейнером IоС и поддержка архитектуры REST. Крейг Уоллс обладает особым талантом придумывать весьма интересные примеры, сосредоточенные на особенностях и приемах использования Spring, которые действительно будут полезны читателям. Издание предназначено как для начинающих пользователей фреймворка, так и для опытных пользователей Spring, желающих задействовать новые возможности версии 3.0. Скачать 👉 @java_360

Как в лямбде изменить внешнюю локальную переменную? Это нельзя сделать в лоб. Такой код не скомпилируется, потому что захваченная локальная переменная обязана быть effectively final. Такое требование исходит из следующих причин. Локальная переменная хранится на стеке, а значит время ее жизни в отличие от долгоживущих элементов хипа ограничено скоупом и текущим потоком. Но экземпляр лямбды, захвативший эту переменную, мог бы быть передан наружу и использован для доступа к этой переменной из другого потока и после выхода из метода. Эта проблема решается тем, что в лямбду копируется значение локальной переменной. Такая копия живет независимо, возможно дольше оригинала. Но это решение приведет к сложному поведению из-за возможности работы с неактуальным значением – копия и оригинал станут двумя разными переменными. Поэтому значение должно быть вечно актуально – неизменяемо. Поля экземпляра менять можно, потому что захваченной переменной в этом случае выступает effectively final значение this. Если локальную переменную всё же хочется изменить, решение очевидно – поместить её в кучу. Для этого нужно использовать любого рода обертку: одноэлементный массив, объект-atomic, специально созданный класс с этой переменной как полем. Хак с оберткой решает проблему времени жизни и даёт коду скомпилироваться, но возвращает проблему сложности поведения. Если среда многопоточная, то вероятно порядок операций с этой переменной придется синхронизировать вручную. #Язык #Многопоточность

Подборка каналов для IT специалистов 🎯 Программирование C++📌 https://t.me/cpp_lib Библиотека C/C++ разработчика https://t.me/cpp_knigi Книги для программистов C/C++ Программирование Python 📌 https://t.me/pythonofff Python академия. Учи Python быстро и легко🐍 https://t.me/BookPython Библиотека Python разработчика https://t.me/python_real Python подборки на русском и английском https://t.me/python_360 Книги по Python Rus Java разработка 📌 https://t.me/BookJava Библиотека Java разработчика https://t.me/java_360 Книги по Java Rus GitHub Сообщество 📌 https://t.me/Githublib Интересное из GitHub CodePen 📌 https://t.me/codepen_1 Сообщество пользователей CodePen Базы данных (Data Base) 📌 https://t.me/database_info Все про базы данных Мобильная разработка: iOS, Android 📌 https://t.me/developer_mobila Мобильная разработка Фронтенд разработка 📌 https://t.me/frontend_1 Подборки для frontend разработчиков Разработка игр 📌 https://t.me/game_devv Все о разработке игр Вакансии 📌 https://t.me/sysadmin_rabota Системный Администратор https://t.me/progjob Вакансии в IT Чат программистов📌 https://t.me/developers_ru Библиотеки 📌 https://t.me/book_for_dev Книги для программистов Rus https://t.me/programmist_of Книги по программированию https://t.me/proglb Библиотека программиста https://t.me/bfbook Книги для программистов БигДата, машинное обучение 📌 https://t.me/bigdata_1 Data Science, Big Data, Machine Learning, Deep Learning Программирование 📌 https://t.me/bookflow Лекции, видеоуроки, доклады с IT конференций https://t.me/coddy_academy Полезные советы по программированию QA, тестирование 📌 https://t.me/testlab_qa Библиотека тестировщика Шутки программистов 📌 https://t.me/itumor Шутки программистов Защита, взлом, безопасность 📌 https://t.me/thehaking Канал о кибербезопасности Книги, статьи для дизайнеров 📌 https://t.me/ux_web Статьи, книги для дизайнеров https://t.me/arhitekturamira World Architecture Системное администрирование 📌 https://t.me/tipsysdmin Типичный Сисадмин (фото железа, было/стало) https://t.me/sysadminof Книги для админов, полезные материалы https://t.me/i_odmin Все для системного администратора https://t.me/i_odmin_book Библиотека Системного Администратора https://t.me/i_odmin_chat Чат системных администраторов https://t.me/i_DevOps DevOps: Пишем о Docker, Kubernetes и др. Английский 📌 https://t.me/UchuEnglish Английский с нуля Математика 📌 https://t.me/Pomatematike Канал по математике Арбитраж трафика 📌 https://t.me/partnerochkin CPA и арбитраж трафика Крипта 📌 https://t.me/bitkoinoff Новости криптовалют Метавселенная, GameFi, Crypto 📌 https://t.me/metaverse360 DeepFake 📌 https://t.me/deepfakenow Публикуем deepfake видео Мир технологий 📌 https://t.me/mir_teh Видео из мира технологий Excel лайфхак📌 https://t.me/Excel_lifehack

Как объявить переменное количество аргументов метода? Для этого используется аргумент-массив. В нем может находиться любое количество элементов. Еще с Java 5 для этого случая добавился синтаксический сахар: Variable-length argument (vararg). Три точки ... ставятся между типом и именем переменной, и становится можно передать любое количество аргументов, не упаковывая их в массив. На уровне байткода применение массива и варарга не отличаются: vararg-параметр Foo... превращается в параметр-массив Foo[], на этапе вызова подставляется неявное инстанцирование и заполнение массива. Чтобы избежать неоднозначностей, на vararg наложено ограничение: им может быть только один последний аргумент. Vararg, как массив, может быть пустым. Иногда это приводит к неочевидному поведению. Допустим, имеем две перегрузки метода с аргументами int... и float.... Вызов такого метода без параметров попадает в вариант с int, как с более специфичным типом. Наличие же перегрузки с несовместимыми типами, например int... и boolean..., приводит при вызове к ошибке компиляции «Ambiguous method call». Когда типом варарга используется generic-параметр, возникает warning «Possible heap pollution from parameterized vararg type». Вам нужно убедиться, что вы понимаете в чем этот риск, что ваш код не приводит к heap pollution, и уведомить об этом компилятор аннотацией @SafeVarargs. #Язык

💣 Хочешь попробовать себя в Android-разработке? 📌 Приглашаем 16 июня в 20:00 на открытый урок в OTUS, где мы создадим прило
💣 Хочешь попробовать себя в Android-разработке? 📌 Приглашаем 16 июня в 20:00 на открытый урок в OTUS, где мы создадим приложение для трекинга воды. Занятие проведет Даниил Попов, разработчик в компании Bolt Technology OÜ (Эстония). 🚀 Мы разработаем приложение для отслеживания количества воды, которое человек пьет в сутки. Для этого мы нарисуем стаканчик и будем заполнять его по мере того, как пользователь отмечает сколько воды он выпил за сутки. 💻 Вебинар пройдет в рамках специализации «Android Developer». Это возможность оценить материалы курса и познакомиться с экспертом. ❇️ Для участия необходимо зарегистрироваться

Промежуточные операции Stream API Все методы Stream API можно разделить на две группы: промежуточные и терминальные (конечные). Промежуточные операции следует воспринимать как «отложенные», т.е. они не меняют сами данные, а только задают правила их изменения. А терминальные как раз инициируют всю цепочку преобразований и возвращают модифицированные данные. Рассмотрим промежуточные операции. Все промежуточные операции возвращают типизированный интерфейс Stream<>.

Какие промежуточные методы работы со стримами вы знаете? filter() отфильтровывает записи, возвращая только записи, соответствующие условию; skip() позволяет пропустить определённое количество элементов в начале; distinct() возвращает стрим без дубликатов (для метода equals()); map() преобразует каждый элемент; peek() возвращает тот же стрим, применяя к каждому элементу функцию; limit() позволяет ограничить выборку определенным количеством первых элементов; sorted() позволяет сортировать значения либо в натуральном порядке, либо задавая Comparator; mapToInt(), mapToDouble(), mapToLong() - аналоги map() возвращающие стрим числовых примитивов; flatMap(), flatMapToInt(), flatMapToDouble(), flatMapToLong() - похожи на map(), но могут создавать из одного элемента несколько. Для числовых стримов дополнительно доступен метод mapToObj(), который преобразует числовой стрим обратно в объектный.

▶️ Более 70 000 учеников уже присоединились к beONmax! https://beonmax.com Изучайте самые популярные онлайн профессии на прак
▶️ Более 70 000 учеников уже присоединились к beONmax! https://beonmax.com Изучайте самые популярные онлайн профессии на практике. Более 50+ курсов по верстке и созданию сайтов, программированию, базам данных, мобильной разработке, дизайну, продвижению и др. 🔹 HTML / CSS 🔹 JavaScript, React JS 🔹 PHP / MySQL 🔹 Python, Django 🔹 Java, Kotlin, Android, C# 🔹 WordPress 🔹 Figma, Photoshop, Illustrator 🔹 Реклама Yandex, Google, VK и многое другое… Удобно и выгодно  Обучайтесь на любом курсе по единой подписке - от 90 руб. в месяц! Вы получите: ☑️ Обучение от экспертов-практиков с реальным опытом ☑️ Десятки крутых проектов в свое портфолио ☑️ Сертификаты по итогам каждого курса ☑️ Рекомендации по трудоустройству или поиску заказов ☑️ Вопросы и обмен опытом в сообществе ☑️ Доступ онлайн 24/7 Отзывы реальных учеников, прошедших обучение на beONmax 🚀 ▶️ Начать легко и доступно каждому, даже если вы новичок. Просто регистрируйтесь и проходите первые открытые уроки — Присоединяйтесь к beONmax!

На какие области делится память JVM? Следует помнить, что это внутренние особенности HotSpot (и её opensource-версии OpenJDK). В других виртуальных машинах (например в Android) всё может быть абсолютно по-другому. Области-поколения кучи вообще зависят от используемого алгоритма сборки мусора, и могут отличаться в рамках одной и той же реализации виртуальной машины. Как было сказано в предыдущих постах, некоторые сборщики не пользуются понятием поколений совсем. Stack – место под примитивы и ссылки на объекты (но не сами объекты). Хранит локальные переменные и возвращаемые значения функций. Здесь же хранятся ссылки на объекты пока те конструируются. Все данные в стеке – GC roots. Освобождается сразу на выходе из функции. Принадлежит потоку, размер по-умолчанию указывается параметром виртуальной машины -Xss, но при создании потока программно можно указать отличное значение. Подробнее. PermGen – В этой области хранятся загруженные классы (экземпляры класса Class<T>). Здесь же с Java 7 хранится пул строк. Изначально размера -XX:PermSize, растет динамически до -XX:MaxPermSize. Не считается частью кучи. Metaspace – с Java 8 заменяет permanent generation. Отличие в том, что по умолчанию metaspace ограничен только размерами доступной на машине памяти, но так же как PermGen может быть ограничен, параметром -XX:MaxMetaspaceSize. Heap – куча, вся managed-память, в которой хранятся все пользовательские объекты. Все следующие разделы – части кучи. Параметры -Xms, -Xmn и -Xmx устанавливают начальный, минимальный и максимальный размеры хипа соответственно. Eden, New Generation, Old Generation и другие – специфичные для сборщика мусора части кучи, поколения. Могут быть разные, но общий подход сохраняется: долго живущий объект постепенно двигается во всё более старое поколение; сборка мусора в разных поколениях происходит раздельно; чем поколение старше, тем сборка в нём реже, но и дороже. Подробнее. Хотя устройство памяти – это детали реализации виртуальной машины, для Java-разработчика знания о них несут практическую пользу. Эти знания необходимы для передачи правильных значений параметров JVM, что в свою очередь спасает от просадок производительности GC и остановок с OutOfMemoryError. #JVM

Что такое Reflection и как его использовать? Reflection, рефлексия – это средства манипуляции данными на основе знания о структуре классов этих данных, инструменты метапрограммирования. Класс Class<T> используется как точка входа в мир рефлекшена. Его экземпляры предоставляют саму метаинформацию о содержимом класса и основные методы для работы с ним. Все классы относящиеся Java Reflection находятся в пакетах java.lang и java.lang.reflect. Экземпляр класса Class можно получить тремя способами: 🔘 Литералом .class; 🔘 Статическим фабричным методом Class.forName(); 🔘 Методом getClass() экземпляров класса. Использование Reflection API медленное и небезопасное. Оно позволяет ломать инвариантность состояний экземпляра, нарушать инкапсуляцию, и даже менять финальные поля. Использовать рефлексию естественно в тестовом коде, в инструментах разработки, в фреймворках (особенно в связке с runtime-аннотациями). Рефлекшн в ординарном бизнес-коде обычно говорит о больших проблемах проектирования. Нередко на интервью просят продемонстрировать пример использования рефлекшна. Один из самых близких для backend-разработчика примеров – инициализация классов-конфигураций в Spring Framework. Фреймворк с помощью рефлекшна сканирует внутренности таких классов. Поля и методы, помеченные специальными аннотациями, воспринимаются как объявления элементов экосистемы фреймворка. #Классы

🔥Хардкорный тест по Java🔥 ⭕️Осторожно! Из-за этого курса можно стать супервостребованным Java-разработчиком! Пройди тест из
🔥Хардкорный тест по Java🔥 ⭕️Осторожно! Из-за этого курса можно стать супервостребованным Java-разработчиком! Пройди тест из 20 вопроса и испытай себя. 🦸🏼‍♂️62 компании-партнера ждут, когда ты овладеешь этим мастерством. Проходи вступительный тест курса "Java Developer. Professional" и помни большая сила — большая ответственность! 📌ПРОЙТИ ТЕСТ - https://otus.pw/dvKm/

Сегодня паттерн Посетитель в Java уже не нужен – лучше использовать переключатели паттернов В современном языке Java паттерн
Сегодня паттерн Посетитель в Java уже не нужен – лучше использовать переключатели паттернов В современном языке Java паттерн Посетитель (Visitor) уже не нужен. Он отлично компенсируется использованием запечатанных типов и переключателей, использующих сопоставление с шаблоном – в таком случае те же цели достигаются проще и меньшим объемом кода. Далее

Чем анонимный внутренний класс отличается от лямбды? Лямбда-выражение имеет более легковесный синтаксис. Не нужно явно указывать тип функционального интерфейса, который лямбда реализует – он автоматически выведется из контекста. Лямбда-выражения добавлены в язык в первую очередь как синтаксический сахар. С другой стороны, у класса есть поля. Экземпляр анонимного класса сохраняет свое состояние между вызовами, и меняет его при необходимости. Для лямбды доступен лишь захват и effectively final использование внешних переменных. Лямбдой реализуется только функциональный интерфейс. Функциональный интерфейс – это тип с единственным абстрактным методом. Анонимным классом же можно расширить любой расширяемый класс или реализовать интерфейс с любым количеством абстрактных методов. Анонимный класс создает новый скоуп, лямбда работает в текущем. Это значит, что объявление переменной с именем, которое уже используется снаружи, в лямбде вызовет ошибку компиляции «variable is already defined», в анонимном классе скроет (shadowing) внешнюю переменную. С точки зрения реализации JVM, для лямбды не создается дополнительного .class файла, как это происходит для анонимного класса. Соответственно, не происходит и обычной загрузки и верификации класса. Вместо этого используется механизм invokedynamic и класс генерируется на лету с помощью LambdaMetafactory. Так что лямбда-выражения обычно работают быстрее. Внутренности реализации подробно разобраны например в этой статье.

Ты Java программист и устал серфить интернет в поисках вакансий? Тяжело найти ту самую работу, которая будет соответствовать
Ты Java программист и устал серфить интернет в поисках вакансий? Тяжело найти ту самую работу, которая будет соответствовать и отвечать требованиям твоего уровня? Java Job - канал, который поможет решить эти вопросы На канале ты найдешь: свежие и актуальные вакансии, с достойными зарплатами и условиями труда стажировки, бесплатные курсы, работа для джунов, мидлов и сеньеров по всему миру примеры вопросов с собеседований, интересные задачи и обучающий материал для повышения скилла Подписывайся на наш канал, цени свое время!

Классические задачи Computer Science на языке Java Копец Дэвид (2021) Cтолкнулись с «неразрешимой» проблемой при разработке п
Классические задачи Computer Science на языке Java Копец Дэвид (2021) Cтолкнулись с «неразрешимой» проблемой при разработке программного обеспечения? Скорее всего, кто-то уже справился с этой задачей, и вы можете не ломать голову. Дэвид Копец собрал наиболее полезные готовые решения, принципы и алгоритмы. «Классические задачи Computer Science на языке Java» — это мастер-класс по программированию, содержащий 55 практических примеров, затрагивающих самые актуальные темы: базовые алгоритмы, ограничения, искусственный интеллект и многое другое. В этой книге: - Рекурсия, мемоизация и битовые манипуляции. - Поисковые, графовые и генетические алгоритмы. - Проблемы ограничений. - Кластеризация методом k-среднего, нейронные сети и состязательный поиск. Скачать 👉 @java_360

Вам нужно обновить или купить новые компьютеры или их комплектующие для офиса? Хотите, чтобы покупка была выгодной и удобной?
Вам нужно обновить или купить новые компьютеры или их комплектующие для офиса? Хотите, чтобы покупка была выгодной и удобной? Сейчас в Ситилинк проходит акция «Идеальный ПК» специально для юридических лиц До конца июня при заказе комплектующих на сайте Ситилинк вы получаете: • 100% скидку на сборку заказанных машин • до 3% бонусов на вашу клубную карту клиента Переходи по ссылке ниже и соберите свой идеальный ПК https://clck.ru/pe3zN

Назовите проблемы стандартной сериализации. Стандартная сериализация Java при всей своей гибкости обладает рядом проблем. Дефолтная сериализация фактически добавляет все приватные поля в экспортируемый интерфейс, и ограничивает будущую гибкость реализации. От этого спасает serialization proxy. Десериализация работает как скрытый конструктор, мимо настоящего, который обеспечивает консистентность состояния. Не сработают даже инициализаторы: поле, определенное как int foo=42, после десериализации будет хранить значение 0. Сериализация усложняет тестирование – между разными версиями класса должна соблюдаться семантическая совместимость. Эта проблема актуальна для любой реализации персистентного хранения объектов. Неаккуратно определенная десериализация создает дыры в безопасности. Например, сериализация объекта со слишком глубоким графом объектов-полей может привести к StackOverflowError. Злоумышленник положит вашу систему, подставив такой искусственный объект. Для защиты от различных уязвимостей в Java 8 добавлен механизм сериализационного фильтрования. Встроенная реализация сериализует все классы одинаково хорошо. Естественно, за универсальность нужно платить, и для каждого частного случая производительность не идеальна и может быть улучшена частной реализацией. #Сериализация

Как создать рандомную строку в java? Как вариант используйте StringBuilder и цикл чтобы создать рандомную строку в Java, ниже
Как создать рандомную строку в java? Как вариант используйте StringBuilder и цикл чтобы создать рандомную строку в Java, ниже пример кода как пример. import java.security.SecureRandom; public class Main { public static void main(String args[]) { // Символы строки String alpha = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; // Длина строка int len = 5; StringBuilder sb = new StringBuilder(len); SecureRandom rnd = new SecureRandom(); for (int i = 0; i < len; i++) { sb.append(alpha.charAt(rnd.nextInt(alpha.length()))); } // Вывод: 5ezs1 System.out.println(sb.toString()); } } #java Подписывайтесь на канал 👉@coddy_academy

Газпромбанк приглашает тебя на онлайн-лекцию о старте карьеры в IT! 9 июня в 18:00 (по МСК) Ян Ашенкампф поделится инсайтами
Газпромбанк приглашает тебя на онлайн-лекцию о старте карьеры в IT! 9 июня в 18:00 (по МСК) Ян Ашенкампф поделится инсайтами о поиске идеального работодателя и преодоления трудностей на первой работе. На примере личного опыта Ян расскажет про путь до руководителя разработки департамента ИТ инвестиционного бизнеса Газпромбанка. Что тебя ждет: • лекция о построении карьеры в IT, лайфхаки для прохождения собеседований и адаптации на рабочем месте для джунов; • возможность задать вопросы лектору и завести полезные знакомства; • мерч и сертификат об участии! Лекция пройдет в Zoom (только live) – регистрируйся: https://vk.cc/cdZ3PU