Когда требуется конкатенировать строки с использованием
Stream, можно выбрать один из двух методов: Stream.reduce() или Stream.collect(Collectors.joining()).Пример с
Stream.reduce(): List<String> list = List.of("Str1", "Str2", "Str3");
String result = list.stream().reduce("", (a, b) -> a + b);
System.out.println(result); // Str1Str2Str3Пример с
Collectors.joining(): List<String> list = List.of("Str1", "Str2", "Str3");
String result = list.stream().collect(Collectors.joining());
System.out.println(result); // Str1Str2Str3Использование
reduce() для конкатенации строк не является оптимальным с точки зрения производительности. При каждом вызове операции +, создается новая строка, так как строки в Java неизменяемы. Это приводит к увеличению нагрузки на память из-за создания множества промежуточных объектов.В свою очередь, метод
Collectors.joining() использует StringBuilder для сборки строк, что значительно эффективнее. Он избегает создания лишних объектов и снижает потребление памяти.#java #Stream #reduce #joining
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤1
// Для импорта конкретного статического члена
import static package.Clazz.member;
// Для импорта всех статических членов класса
import static package.Clazz.*;
Пример:
public class MathUtils {
public static final double PI = 3.141592653589793;
public static int add(int a, int b) {
return a + b;
}
public static int subtract(int a, int b) {
return a - b;
}
}
import static MathUtils.PI;
import static MathUtils.add;
public class Main {
public static void main(String[] args) {
System.out.println("Value of PI: " + PI);
System.out.println("Addition result: " + add(5, 3));
}
}
import static MathUtils.*;
public class Main {
public static void main(String[] args) {
System.out.println("Value of PI: " + PI);
System.out.println("Addition result: " + add(5, 3));
System.out.println("Subtraction result: " + subtract(5, 3));
}
}
Плюсы: читаемость, компактность кода.
Минусы:
- не очевидно откуда берется поле или метод
- импорт всех статических членов может привести к конфликтам имен, если разные классы содержат статические члены с одинаковыми именами
#java #static #import
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥4❤1
Предположим, есть метод, который может выбросить
IOException и FileNotFoundException в какой последовательности должны идти блоки catch? Сколько блоков catch будет выполнено?Общее правило: обрабатывать исключения нужно от «младшего» к старшему. То есть нельзя поставить в первый блок
catch(Exception ex) {}, иначе все дальнейшие блоки catch() уже ничего не смогут обработать, так как любое исключение будет соответствовать обработчику catch(Exception ex).Таким образом, исходя из факта, что
FileNotFoundException extends IOException сначала нужно обработать FileNotFoundException, а затем уже IOException:
void method() {
try {
//...
} catch (FileNotFoundException ex) {
//...
} catch (IOException ex) {
//...
}
}
В Java 7 стала доступна новая языковая конструкция, с помощью которой можно перехватывать несколько исключений одним блоком
catch:
try {
//...
} catch(IOException | SQLException ex) {
//...
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤3🔥1
Исключения делятся на несколько классов, но все они имеют общего предка — класс
Throwable, потомками которого являются классы Exception и Error.Ошибки (Errors) представляют собой более серьёзные проблемы, которые, согласно спецификации Java, не следует обрабатывать в собственной программе, поскольку они связаны с проблемами уровня JVM. Например, исключения такого рода возникают, если закончилась память доступная виртуальной машине.
Исключения (Exceptions) являются результатом проблем в программе, которые в принципе решаемы, предсказуемы и последствия которых возможно устранить внутри программы. Например, произошло деление целого числа на ноль.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤1
Generics - это технический термин, обозначающий набор свойств языка позволяющих определять и использовать обобщенные типы и методы. Обобщенные типы или методы отличаются от обычных тем, что имеют типизированные параметры.
Примером использования обобщенных типов может служить Java Collection Framework. Так, класс
LinkedList<E> - типичный обобщенный тип. Он содержит параметр E, который представляет тип элементов, которые будут храниться в коллекции. Создание объектов обобщенных типов происходит посредством замены параметризированных типов реальными типами данных. Вместо того, чтобы просто использовать LinkedList, ничего не говоря о типе элемента в списке, предлагается использовать точное указание типа LinkedList<String>, LinkedList<Integer> и т.п.Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤2
try — данное ключевое слово используется для отметки начала блока кода, который потенциально может привести к ошибке. catch — ключевое слово для отметки начала блока кода, предназначенного для перехвата и обработки исключений в случае их возникновения. finally — ключевое слово для отметки начала блока кода, который является дополнительным. Этот блок помещается после последнего блока catch. Управление передаётся в блок finally в любом случае, было выброшено исключение или нет.Общий вид конструкции для обработки исключительной ситуации выглядит следующим образом:
try {
//код, который потенциально может привести к исключительной ситуации
}
catch(SomeException e ) { //в скобках указывается класс конкретной ожидаемой ошибки
//код обработки исключительной ситуации
}
finally {
//необязательный блок, код которого выполняется в любом случае
}
Возможно ли использование блока try-finally (без catch)?
Такая запись допустима, но смысла в такой записи не так много, всё же лучше иметь блок
catch, в котором будет обрабатываться необходимое исключение.Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤6❤🔥1
Множитель создает зависимость значения хэш кода от очередности обработки полей, что в итоге порождает лучшую хэш функцию.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8❤2👍1
Часто нам нужно проверить является ли объект экземпляром определенного класса, например в методе equals(). Существует несколько способов это сделать. Разберём два из них.
instanceof проверяет, является ли ссылка на объект в левой части экземпляром типа в правой части или каким-либо подтипом.
getClass() == ... проверяет, идентичны ли типы.
Выбирать способ проверки типа нужно исходя из решаемой задачи. В методе equals() можно использовать оба способа в зависимости от того собираетесь ли вы позволять сравнивать объект определенного класса и объект класса наследника.
class Parent { }
class Child extends Parent { }
public class Test {
public static void main(String[] args) {
Parent parent = new Parent();
Child child = new Child();
System.out.println(parent instanceof Parent); // true
System.out.println(child instanceof Parent); // true
System.out.println(parent.getClass() == Parent.class); // true
System.out.println(child.getClass() == Parent.class); // false
}
}
#java #instanceof #getClass
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤4
Общий совет: выбирать поля, которые с большой долью вероятности будут различаться. Для этого необходимо использовать уникальные, лучше всего примитивные поля, например, такие как
id, uuid. При этом нужно следовать правилу, если поля задействованы при вычислении hashCode(), то они должны быть задействованы и при выполнении equals().hashCode()?Да, могут. Метод
hashCode() не гарантирует уникальность возвращаемого значения. Ситуация, когда у разных объектов одинаковые хэш коды называется коллизией. Вероятность возникновения коллизии зависит от используемого алгоритма генерации хэш кода.Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤4
&& и || называются сокращёнными логическими операторами AND и OR соответственно, или операторами короткой схемы вычислений. В спецификации Java их ещё зовут условными. Значения их операндов могут быть только булева типа.В отличие от двойных, одиночные
& и | называются операторами полной схемы вычислений. Значения их операндов могут быть как только булевыми, так и только целочисленными (вместе с оператором ^ они используются в побитовых операциях).В чём разница?
В том, что для операторов
& и | всегда вычисляются значения обоих операндов, а при работе операторов && и || второй операнд вычисляется только по необходимости.То есть иногда результат выражения однозначно определён уже по первому операнду:
&& равен false, то второй не вычисляется, так как уже понятно, что результат всего выражения будет false.|| равен true, то второй не вычисляется, так как уже понятно, что || вернёт true.&& и || используют как операторы булевой логики. Они оперируют значениями только булева типа и применяются только в логических выражениях.Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤2
Скачать файл используя Java NIO
Пакет Java NIO предлагает возможность передавать байты между двумя каналами без их буферизации в памяти приложения.
Чтобы прочитать файл по нашему URL-адресу, мы создадим новый
Байты, прочитанные из
Используем метод
Методы
В системах Linux и UNIX эти методы используют метод нулевого копирования, который уменьшает количество переключений контекста между режимом ядра и пользовательским режимом.
Пакет Java NIO предлагает возможность передавать байты между двумя каналами без их буферизации в памяти приложения.
Чтобы прочитать файл по нашему URL-адресу, мы создадим новый
ReadableByteChannel из потока URL-адреса.Байты, прочитанные из
ReadableByteChannel, будут переданы в FileChannel, соответствующий загружаемому файлу.Используем метод
transferFrom() из класса ReadableByteChannel для загрузки байтов с заданного URL-адреса в наш FileChannel.Методы
transferTo() и transferFrom() более эффективны, чем простое чтение из потока с использованием буфера. В зависимости от операционной системы данные могут быть перенесены непосредственно из кэша файловой системы в наш файл без копирования каких-либо байтов в память приложения.В системах Linux и UNIX эти методы используют метод нулевого копирования, который уменьшает количество переключений контекста между режимом ядра и пользовательским режимом.
👍8❤4🔥3
Есть несколько видов ссылок на методы:
1️⃣ Ссылка на статический метод
Используется для вызова статических методов класса.
Синтаксис:
ContainingClass::staticMethodName
Пример:
import java.util.function.Function;
public class MethodReferenceExample {
public static void main(String[] args) {
Function<String, Integer> converter = Integer::parseInt;
Integer number = converter.apply("123");
System.out.println(number); // 123
}
}
2️⃣ Ссылка на метод экземпляра конкретного объекта
Используется для вызова методов экземпляра объекта.
Синтаксис:
containingObject::instanceMethodName
Пример:
import java.util.Arrays;
import java.util.List;
public class MethodReferenceExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(System.out::println);
}
}
3️⃣ Ссылка на метод экземпляра произвольного объекта определенного типа
Синтаксис:
ContainingType::methodName
Пример:
import java.util.function.Function;
public class FunctionExample {
public static void main(String[] args) {
Function<String, Integer> stringLength = String::length;
Integer length = stringLength.apply("Hello, World!");
System.out.println(length); // 13
}
}
Отличие от предыдущего вида в том, что в предыдущем ссылка на метод получается у конкретного объекта, стало быть вызовется метод этого объекта, а в нашем случае при получении ссылки на метод, мы знаем только метод, но объект у которого вызовется этот метод еще не известен, он будет предоставлен при вызове метода. Код выше будет означать "Hello, World!".length()
4️⃣ Ссылка на конструктор
Используется для вызова конструктора.
Синтаксис:
ClassName::new
Пример:
import java.util.function.Supplier;
public class MethodReferenceExample {
public static void main(String[] args) {
Supplier<MethodReferenceExample> exampleSupplier = MethodReferenceExample::new;
MethodReferenceExample example = exampleSupplier.get();
System.out.println(example); // MethodReferenceExample@<hashcode>
}
}
#java #MethodReference
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤4
🟢 При разработке игр на Java требуется работать со звуком и музыкой.🟢 Нужно подключить звуковые звуки (эффекты и музыку) к игровому проекту.🟢 Реализовать звуковой сигнал в нужные моменты (например, выстрел, прыжок, победа).🟢 Обеспечить громкость и возможность паузы/продолжения музыки.🟢 Оптимизировать загрузку и меры для минимизации задержек и нагрузки на систему.
🟢 javax.sound.sampled позволяет работать с аудиофайлами формата WAV, AIFF и др.
🟢 Например, LWJGL OpenAL , TinySound , JOAL — более мощные и гибкие решения для работы со звуком в играх, 3D-звуке, резервных источниках и других возможностях.
🟢 Если используются игровые движки (например, LibGDX), он обычно предоставляет собственный удобный API для работы со звуком.
import javax.sound.sampled.*;
import java.io.File;
import java.io.IOException;
public class SoundPlayer {
private Clip clip;
// Загрузка и подготовка звукового файла
public void loadSound(String filepath) {
try {
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File(filepath));
clip = AudioSystem.getClip();
clip.open(audioInputStream);
} catch (UnsupportedAudioFileException | IOException | LineUnavailableException e) {
System.err.println("Ошибка загрузки звука: " + e.getMessage());
}
}
// Воспроизведение звука
public void play() {
if (clip == null) return;
if (clip.isRunning()) clip.stop();
clip.setFramePosition(0);
clip.start();
}
// Остановка звука
public void stop() {
if (clip != null && clip.isRunning()) {
clip.stop();
}
}
// Регулировка громкости (от 0.0 до 1.0)
public void setVolume(float volume) {
if (clip == null) return;
FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN);
float dB = (float)(Math.log10(volume) * 20);
gainControl.setValue(dB);
}
public static void main(String[] args) {
SoundPlayer sound = new SoundPlayer();
sound.loadSound("jump.wav"); // Замените на путь к вашему звуковому файлу
sound.setVolume(0.5f);
sound.play();
// Для демонстрации задержка перед завершением программы
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
sound.stop();
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤5🔥1👏1
🟢 Сохранить состояние игрового объекта (например, класс Player) в файл.🟢 Загрузить данные из файла при старте игры.🟢 Обеспечить корректную работу даже при изменениях в структуре класса.🟢 Обработать возможные ошибки ввода-вывода.
🟢 Класс реализует интерфейс Serializable, и с помощью ObjectOutputStream и ObjectInputStream объекты записываются и читаются из файла.
🟢 Сериализация в человекочитаемый формат с помощью библиотек, например, Gson, Jackson (для JSON) или JAXB (для XML). Это удобно для отладки и совместимости.
🟢 Реализация собственных методов записи и чтения данных, например, через DataOutputStream/DataInputStream, что даёт больше контроля над форматом и размером файла.
import java.io.*;
class Player implements Serializable {
private static final long serialVersionUID = 1L;
String name;
int level;
int score;
public Player(String name, int level, int score) {
this.name = name;
this.level = level;
this.score = score;
}
@Override
public String toString() {
return "Player{name='" + name + "', level=" + level + ", score=" + score + '}';
}
}
public class SaveLoadGame {
public static void savePlayer(Player player, String filename) {
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filename))) {
oos.writeObject(player);
System.out.println("Прогресс сохранён.");
} catch (IOException e) {
System.err.println("Ошибка при сохранении: " + e.getMessage());
}
}
public static Player loadPlayer(String filename) {
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filename))) {
return (Player) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
System.err.println("Ошибка при загрузке: " + e.getMessage());
return null;
}
}
public static void main(String[] args) {
Player player = new Player("Игрок1", 5, 1500);
String saveFile = "player.dat";
savePlayer(player, saveFile);
Player loadedPlayer = loadPlayer(saveFile);
if (loadedPlayer != null) {
System.out.println("Загруженный игрок: " + loadedPlayer);
}
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤5🔥2
Цикл for со счетчиком
for (initialization; condition; update) {
// тело цикла
}
Описание:
initialization: выполняется один раз перед началом цикла. Обычно используется для инициализации счетчика.
condition: проверяется перед каждой итерацией. Если условие истинно, выполняется тело цикла.
update: выполняется после каждой итерации. Обычно используется для изменения счетчика.
int[] numbers = {1, 2, 3, 4, 5};
for (int i = 0; i < numbers.length; i++) {
System.out.println(i + ": " + numbers[i]);
}
Применение:
Используется, когда известно количество итераций заранее. Часто применяется для работы с массивами или коллекциями, когда нужен индекс.
Цикл for-each
for (type element : collection) {
// тело цикла
}
Описание:
Итерируется по каждому элементу коллекции (например, массива, списка, множества). Обеспечивает удобный способ доступа к каждому элементу без использования индексов.
int[] numbers = {1, 2, 3, 4, 5};
for (int number : numbers) {
System.out.println(number);
}
Применение:
Предпочтителен, когда не нужен доступ к индексам элементов.
Цикл while
while (condition) {
// тело цикла
}
Описание:
condition: проверяется перед каждой итерацией. Если условие истинно, выполняется тело цикла.
String code = UUID.randomUUID().toString();
boolean isCodeExists = repo.isCodeExists(code);
while (isCodeExists) {
code = UUID.randomUUID().toString();
isCodeExists = repo.isCodeExists(code);
}
repo.saveCode(code);
Применение:
Используется, когда количество итераций неизвестно заранее и определяется во время выполнения программы. Цикл может не выполниться ни одного раза.
Цикл do-while
do {
// тело цикла
} while (condition);
Описание:
Выполняет тело цикла хотя бы один раз, а затем проверяет условие condition. Если условие истинно, цикл повторяется.
String command;
do {
command = readCommand();
doCommand(command);
} while (!"EXIT".equals(command));
Применение:
Используется, когда тело цикла должно выполниться хотя бы один раз, независимо от условия. Часто применяется для меню или ввода данных, где сначала нужно выполнить действие, а затем проверить условие.
#java #cycles #for #while
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤1
Function<T,R> — функциональный интерфейс, который умеет принимать объект типа T и возвращать R. У него есть два метода-комбинатора:
default <V> Function<V,R> compose(Function<? super V,? extends T> before)
default <V> Function<T,V> andThen(Function<? super R,? extends V> after)
f.compose(g) → сначала выполняет g, потом результат передаёт в f.f.andThen(h) → сначала выполняет f, потом h.
import java.util.function.Function;
public class ComposeAndThenDemo {
public static void main(String[] args) {
Function<String, Integer> parse = Integer::parseInt;
Function<Integer, Double> half = i -> i / 2.0;
Function<Double, String> toStr = d -> "Result: " + d;
// Составляем: сначала parse, затем half, затем toStr
Function<String, String> pipeline =
toStr
.compose(half) // toStr(half(x))
.compose(parse); // toStr(half(parse(x)))
// То же самое через andThen:
Function<String, String> pipeline2 =
parse
.andThen(half) // half(parse(x))
.andThen(toStr); // toStr(half(parse(x)))
System.out.println(pipeline.apply("42")); // Result: 21.0
System.out.println(pipeline2.apply("42")); // Result: 21.0
}
}
▸
compose строит цепочку справа-налево (parse → half → toStr).▸
andThen — слева-направо (parse → half → toStr).Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤4🔥1
Heap (куча) используется Java Runtime для выделения памяти под объекты и классы. Создание нового объекта также происходит в куче. Это же является областью работы сборщика мусора. Любой объект, созданный в куче, имеет глобальный доступ и на него могут ссылаться из любой части приложения.
Stack (стек) это область хранения данных также находящееся в общей оперативной памяти (RAM). Всякий раз, когда вызывается метод, в памяти стека создается новый блок, который содержит примитивы и ссылки на другие объекты в методе. Как только метод заканчивает работу, блок также перестает использоваться, тем самым предоставляя доступ для следующего метода. Размер стековой памяти намного меньше объема памяти в куче. Стек в Java работает по схеме LIFO (Последний-зашел-Первый-вышел)
Различия между Heap и Stack памятью:
✔️ Куча используется всеми частями приложения, в то время как стек используется только одним потоком исполнения программы.
✔️ Всякий раз, когда создается объект, он всегда хранится в куче, а в памяти стека содержится лишь ссылка на него. Память стека содержит только локальные переменные примитивных типов и ссылки на объекты в куче.
✔️ Объекты в куче доступны с любой точки программы, в то время как стековая память не может быть доступна для других потоков.
✔️ Стековая память существует лишь какое-то время работы программы, а память в куче живет с самого начала до конца работы программы.
✔️ Если память стека полностью занята, то Java Runtime бросает исключение java.lang.StackOverflowError. Если заполнена память кучи, то бросается исключение java.lang.OutOfMemoryError: Java Heap Space.
✔️ Размер памяти стека намного меньше памяти в куче.
✔️ Из-за простоты распределения памяти, стековая память работает намного быстрее кучи.
Для определения начального и максимального размера памяти в куче используются -Xms и -Xmx опции JVM. Для стека определить размер памяти можно с помощью опции -Xss.
#java #heap #stack #memory
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤2🔥1
Java 8 позволяет добавлять неабстрактные реализации методов в интерфейс, используя ключевое слово
default:
interface Example {
int process(int a);
default void show() {
System.out.println("default show()");
}
}
java.lang.Object.Вызывать
default метод интерфейса в реализующем этот интерфейс классе можно используя ключевое слово super вместе с именем интерфейса:
interface Paper {
default void show() {
System.out.println("default show()");
}
}
class Licence implements Paper {
public void show() {
Paper.super.show();
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
🤝10👍2❤1🔥1
Статические методы интерфейса похожи на методы по умолчанию, за исключением того, что для них отсутствует возможность переопределения в классах, реализующих интерфейс.
java.lang.Object нельзя переопределить как статические;null, сортировки коллекций и т.д.Вызывать
static метод интерфейса можно используя имя интерфейса:
interface Paper {
static void show() {
System.out.println("static show()");
}
}
class Licence {
public void showPaper() {
Paper.show();
}
}
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16❤1🔥1
В Java 12 появился новый синтаксис для
switch, который позволяет возвращать значение используя стрелки ->, что делает код более компактным и удобным. Теперь switch может использоваться как выражение, а не только как оператор, что упрощает его применение в логике.Ранее
switch использовался как оператор, и код мог выглядеть громоздко:
String day = "MONDAY";
int numLetters;
switch (day) {
case "MONDAY":
case "FRIDAY":
case "SUNDAY":
numLetters = 6;
break;
case "TUESDAY":
numLetters = 7;
break;
default:
numLetters = 8;
}
System.out.println(numLetters); // Вывод: 6
Со Switch Expressions можно переписать этот код более лаконично:
String day = "MONDAY";
int numLetters = switch (day) {
case "MONDAY", "FRIDAY", "SUNDAY" -> 6;
case "TUESDAY" -> 7;
default -> 8;
};
System.out.println(numLetters); // Вывод: 6
Теперь
switch может вернуть значение, и нет необходимости в break.#java #switch
Please open Telegram to view this post
VIEW IN TELEGRAM
👍22❤3
java.util.stream.Collectors для обработки коллекцийКод:
import java.util.List;
import java.util.stream.Collectors;
public class StreamCollectorsExample {
public static void main(String[] args) {
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Примеры использования Collectors
// Преобразование элементов коллекции в строку
String numbersAsString = numbers.stream()
.map(Object::toString)
.collect(Collectors.joining(", "));
System.out.println("Числа в виде строки: " + numbersAsString);
// Получение среднего значения всех элементов коллекции
double average = numbers.stream()
.collect(Collectors.averagingInt(Integer::intValue));
System.out.println("Среднее значение: " + average);
// Фильтрация элементов и сборка их в новую коллекцию
List<Integer> evenNumbers = numbers.stream()
.filter(num -> num % 2 == 0)
.collect(Collectors.toList());
System.out.println("Четные числа: " + evenNumbers);
// Группировка элементов по условию
System.out.println("Числа по остатку от деления на 3: " + numbers.stream()
.collect(Collectors.groupingBy(num -> num % 3)));
}
}
Collectors из пакета java.util.stream для более эффективной обработки коллекций в Java. Методы класса Collectors предоставляют различные операции для сбора (collecting) элементов из потока данных в различные структуры данных или для выполнения агрегатных операций над элементами потока.Please open Telegram to view this post
VIEW IN TELEGRAM
❤6❤🔥1👍1