Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍20❤1
Манифест файл в JAR (Java ARchive) файле содержит метаинформацию о JAR-файле и его содержимом. Этот файл имеет имя
MANIFEST.MF и находится в директории META-INF JAR-файла. Манифест файл может включать различные атрибуты, которые описывают сам архив, его содержимое, и дополнительные параметры, такие как основной класс для запуска и зависимости.MANIFEST.MF с необходимыми атрибутами.jar для создания JAR файла и включения манифеста. jar cmf MANIFEST.MF myapp.jar -C classes/ .
Добавьте информацию о манифесте в
pom.xml. <build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>com.example.MyMainClass</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
Манифест можно прочитать программно, используя классы из
java.util.jar пакета. import java.io.InputStream;
import java.util.jar.Manifest;
import java.util.jar.Attributes;
public class ReadManifest {
public static void main(String[] args) {
try (InputStream input = ReadManifest.class.getResourceAsStream("/META-INF/MANIFEST.MF")) {
if (input != null) {
Manifest manifest = new Manifest(input);
Attributes attrs = manifest.getMainAttributes();
String version = attrs.getValue("Manifest-Version");
String mainClass = attrs.getValue("Main-Class");
System.out.println("Manifest-Version: " + version);
System.out.println("Main-Class: " + mainClass);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍22❤1
Используется для динамического распределения памяти всех объектов и массивов. Разделена на молодое поколение (Young Generation) и старое поколение (Old Generation).
Eden Space: Новые объекты сначала создаются здесь. Survivor Spaces (S0 и S1): Объекты, которые пережили сборку мусора в Eden Space, перемещаются сюда.
Содержит долгоживущие объекты, которые пережили несколько сборок мусора в Young Generation.
Используется для хранения метаданных классов и методов. В Java 8 заменена на Metaspace, которая выделяется из внеheap-памяти и автоматически увеличивается по мере необходимости.
Содержит фреймы для каждого метода, который выполняется в потоке. Каждый фрейм включает локальные переменные, параметры метода и другие данные.
Это автоматический механизм управления памятью, который освобождает память, занятую объектами, которые больше не используются.
Очищает Young Generation. Быстрая и частая сборка мусора.
Очищает Old Generation и, возможно, всю кучу. Медленнее, так как затрагивает больше объектов.
Использует один поток для сборки мусора. Подходит для небольших приложений или одноядерных систем.
Использует несколько потоков для ускорения сборки мусора. Хорошо подходит для многопроцессорных систем.
Выполняет сборку мусора одновременно с выполнением приложения. Минимизирует паузы, но требует больше ресурсов.
Делит кучу на регионы и выполняет сборку мусора по регионам. Минимизирует паузы и обеспечивает предсказуемую производительность.
Вы можете настроить JVM для использования конкретного сборщика мусора и управлять его параметрами с помощью аргументов командной строки.
java -XX:+UseG1GC -Xms512m -Xmx2g MyApplication
Хотя сборка мусора в Java автоматизирована, можно явным образом вызывать сборку мусора, используя
System.gc(). Однако это лишь рекомендация для JVM, и она не обязана немедленно выполнять сборку мусора. public class GarbageCollectorExample {
public static void main(String[] args) {
// Создание объекта
String str = new String("Hello, World!");
// Обнуление ссылки, объект становится доступным для сборки мусора
str = null;
// Явный вызов сборки мусора (не рекомендуется использовать в продакшн-коде)
System.gc();
System.out.println("Garbage Collector called");
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15❤1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21🔥1
В контексте JVM (Java Virtual Machine) типы данных делятся на два основных класса: примитивные типы данных и ссылочные типы данных.
Примитивные типы данных представляют собой базовые типы, которые не являются объектами и хранят непосредственно значения. Они делятся на числовые типы, логический тип и символьный тип.
Целочисленные типы
byte: 8-битный знаковый целочисленный тип данных (диапазон от -128 до 127).
short: 16-битный знаковый целочисленный тип данных (диапазон от -32,768 до 32,767).
int: 32-битный знаковый целочисленный тип данных (диапазон от -2^31 до 2^31-1).
long: 64-битный знаковый целочисленный тип данных (диапазон от -2^63 до 2^63-1).
Типы с плавающей точкой
float: 32-битный IEEE 754 тип данных с плавающей точкой одинарной точности.
double: 64-битный IEEE 754 тип данных с плавающей точкой двойной точности.
boolean: Представляет логическое значение (true или false).char: 16-битный тип данных, представляющий символ Unicode (диапазон от '\u0000' до '\uffff').Ссылочные типы данных представляют собой объекты и массивы. Они хранят ссылку на область памяти, где хранятся данные объекта или массива.
Любой объектный тип данных является экземпляром класса. Классы могут быть как стандартными (например,
String, Integer), так и пользовательскими.Интерфейсы определяют набор методов, которые должны быть реализованы классами, которые их реализуют.
Массивы могут быть одномерными или многомерными и могут хранить как примитивные, так и ссылочные типы данных.
public class DataTypesExample {
public static void main(String[] args) {
// Примитивные типы данных
byte aByte = 10;
short aShort = 100;
int anInt = 1000;
long aLong = 10000L;
float aFloat = 10.5f;
double aDouble = 10.55;
boolean aBoolean = true;
char aChar = 'A';
// Ссылочные типы данных
String aString = "Hello, World!";
Integer anInteger = 1000;
int[] anArray = {1, 2, 3, 4, 5};
// Вывод примитивных типов данных
System.out.println("byte: " + aByte);
System.out.println("short: " + aShort);
System.out.println("int: " + anInt);
System.out.println("long: " + aLong);
System.out.println("float: " + aFloat);
System.out.println("double: " + aDouble);
System.out.println("boolean: " + aBoolean);
System.out.println("char: " + aChar);
// Вывод ссылочных типов данных
System.out.println("String: " + aString);
System.out.println("Integer: " + anInteger);
System.out.println("Array: " + java.util.Arrays.toString(anArray));
}
}Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14❤2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15
В Java управление памятью и сборка мусора осуществляются автоматически JVM. В случае циклических ссылок, где два или более объектов ссылаются друг на друга, стандартный механизм подсчета ссылок (reference counting) неэффективен, поскольку циклические ссылки не позволят уменьшить счетчик ссылок до нуля. Однако, Java использует более сложные алгоритмы сборки мусора, такие как маркировка-сжатие (mark-and-sweep) и алгоритмы поколения, которые способны обнаруживать и собирать циклически связанные, но неиспользуемые объекты.
Сначала алгоритм начинается с корневых объектов (root objects), таких как статические переменные, локальные переменные в стеке текущего потока, и помечает все доступные объекты, начиная с этих корней.
Все объекты, которые не были помечены в фазе маркировки, считаются недоступными и могут быть собраны мусорщиком.
public class A {
B b;
}
public class B {
A a;
}
public class Main {
public static void main(String[] args) {
A a = new A();
B b = new B();
a.b = b;
b.a = a;
// Теперь a и b ссылаются друг на друга
a = null;
b = null;
// Оба объекта теперь недоступны
// Сборщик мусора сможет их собрать
}
}Когда сборщик мусора выполняет фазу маркировки, он начнет с корневых объектов и будет следовать по всем доступным ссылкам. В этом примере, после того как
a и b установлены в null, объекты класса A и B больше не будут доступны из корневых объектов.Объекты
A и B не помечены как доступные, поэтому они будут собраны сборщиком мусора.Чтобы демонстрировать работу сборщика мусора и показать, что объекты с циклическими ссылками могут быть собраны, можно использовать отладочные инструменты или включить журналирование сборки мусора с помощью аргументов JVM.
java -XX:+PrintGCDetails -XX:+PrintGCTimeStamps Main
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21🔥2
Кроме стека (Stack) и кучи (Heap), в JVM существуют другие области памяти, которые играют важную роль в управлении выполнением программы и ресурсами.
Метаспейс используется для хранения метаданных о классах, таких как информация о методах, полях и других элементах классов. Метаспейс заменил область памяти PermGen (Permanent Generation) в Java 8.
Метаспейс динамически увеличивается по мере необходимости, в отличие от PermGen, который имел фиксированный размер.
Память для метаданных классов теперь выделяется из нативной памяти (вне куче).
Методная область хранит структуру классов, включая метаданные, полевые данные и данные о методах. Используется JVM для загрузки и хранения информации о классах. Включает информацию о типах, полях, методах и конструкторах.
Каждый поток имеет собственный регистр счётчика команд, который указывает на текущую инструкцию, выполняемую потоком. Используется для отслеживания адреса текущей инструкции в программе. Каждому потоку выделяется свой собственный регистр PC.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11❤2
- **Singleton** гарантирует, что класс имеет только один экземпляр в приложении и предоставляет глобальную точку доступа к этому экземпляру.
- **Prototype** позволяет создавать объекты, клонируя существующий объект, что позволяет избежать затрат на создание объектов стандартным способом (через new), особенно если это сложный процесс создания.?
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18🔥7
Пул строк (String Pool) в Java представляет собой особую область памяти в методной области (Metaspace начиная с Java 8), где хранятся строковые литералы.
Поскольку строковые литералы хранятся в пуле строк и не уничтожаются до тех пор, пока JVM работает, злоумышленник, получивший доступ к дампу памяти, может извлечь чувствительные данные, такие как пароли, токены или личные данные, если они были сохранены как строковые литералы.
Злоумышленник может попытаться использовать кэширование строк для проведения атак. Например, если однажды созданный строковый литерал остается в пуле строк, его можно найти и использовать повторно, даже если оригинальная строка больше не используется программой.
Разработчики не могут явно контролировать удаление строк из пула строк. Это может привести к тому, что строковые литералы будут оставаться в памяти дольше, чем нужно, что увеличивает риск их обнаружения при анализе памяти.
Если две строки с одинаковым содержимым помещаются в пул строк, они будут указывать на один и тот же объект. Это может позволить злоумышленнику, знающему об этой особенности, попытаться извлечь чувствительные данные путем поиска дубликатов строк.
public class Example {
public static void main(String[] args) {
String password = "secretPassword123"; // Строка хранится в пуле строк
System.out.println("Password is set.");
}
}Вместо строк используйте массивы символов (
char[]), так как их содержимое можно обнулить после использования. char[] password = new char[] {'s', 'e', 'c', 'r', 'e', 't'};
// Очистка массива после использования
java.util.Arrays.fill(password, '\0'); Используйте специализированные библиотеки, которые обеспечивают более безопасное управление чувствительными данными, такие как Java Cryptography Architecture (JCA).
Не храните пароли, ключи и другие конфиденциальные данные как строковые литералы.
Шифруйте данные перед их хранением или передачей. Даже если данные будут извлечены из памяти, без ключа расшифровки они останутся бесполезными.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16❤2
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍21🔥8❤1
Пул строк в Java создается и управляется JVM для оптимизации использования памяти и повышения производительности при работе со строками. Пул строк представляет собой специальную область памяти, где хранятся строковые литералы и строки, созданные с помощью метода
String.intern().Когда JVM загружает класс, она также загружает все строковые литералы, использованные в этом классе, и добавляет их в пул строк. Каждый раз, когда строковый литерал встречается в коде, JVM проверяет, существует ли он уже в пуле строк. Если да, используется существующая строка; если нет, строка добавляется в пул.
Метод
String.intern() можно использовать для добавления строки в пул строк вручную. Если строка уже существует в пуле, возвращается ссылка на существующую строку; если нет, строка добавляется в пул и возвращается ссылка на нее.public class StringPoolExample {
public static void main(String[] args) {
// Строковые литералы автоматически добавляются в пул строк
String str1 = "Hello";
String str2 = "Hello";
// Проверка, что str1 и str2 ссылаются на один и тот же объект
System.out.println(str1 == str2); // true
// Создание новой строки (не литерала)
String str3 = new String("Hello");
// Проверка, что str3 и str1 ссылаются на разные объекты
System.out.println(str1 == str3); // false
// Использование метода intern()
String str4 = str3.intern();
// Проверка, что str4 и str1 теперь ссылаются на один и тот же объект
System.out.println(str1 == str4); // true
}
}При загрузке класса JVM анализирует все строковые литералы и добавляет их в пул строк, если их там еще нет.
public class Example {
String str = "Hello"; // Литерал добавляется в пул строк при загрузке класса
} Метод
intern() позволяет явно добавить строку в пул строк или получить существующую строку из пула. Это полезно, когда строки создаются динамически и нужно обеспечить, чтобы они находились в пуле строк. String dynamicStr = new String("Dynamic");
String pooledStr = dynamicStr.intern(); // Добавляет строку в пул, если её там еще нет Метод
intern() проверяет, находится ли строка в пуле строк. Если да, он возвращает ссылку на существующую строку. Если нет, строка добавляется в пул строк, и возвращается ссылка на неё.String s1 = new String("Java");
String s2 = s1.intern(); // s2 теперь указывает на строку в пуле строк Пул строк помогает избежать создания дубликатов строк в памяти, что экономит память и повышает производительность. Например, строки, содержащие одно и то же значение, могут использовать одну и ту же память.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥6👍5❤1🤔1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥13❤4👍2
Пул строк в Java не является пустым при старте JAR файла. Он изначально содержит несколько предопределенных строковых литералов, которые JVM использует для своей внутренней работы, а также строковые литералы, используемые в загруженных классах и стандартных библиотеках.
JVM и стандартные библиотеки Java используют различные строковые литералы для своей работы. Эти строки добавляются в пул строк при запуске JVM. Примеры таких строк включают имена основных классов и пакетов (например,
"java/lang/Object", "java/lang/String").Когда классы загружаются JVM, все строковые литералы, используемые в этих классах, добавляются в пул строк. Это включает строки, используемые в JAR файле, а также строки из стандартных библиотек Java, которые загружаются при старте.
Для демонстрации этого можно написать простой код, который проверяет, присутствуют ли определенные строки в пуле строк при старте программы.
public class StringPoolDemo {
public static void main(String[] args) {
// Проверка стандартных строк, которые могут быть в пуле строк
String str1 = "java";
String str2 = "lang";
String str3 = "Object";
// Вывод строк
System.out.println("str1: " + str1);
System.out.println("str2: " + str2);
System.out.println("str3: " + str3);
// Проверка строк в пуле строк
System.out.println("Is 'java' in pool: " + (str1 == "java"));
System.out.println("Is 'lang' in pool: " + (str2 == "lang"));
System.out.println("Is 'Object' in pool: " + (str3 == "Object"));
}
}При запуске JVM загружаются системные классы, такие как
java.lang.Object, java.lang.String, java.lang.System, и другие. Строковые литералы, используемые в этих классах, добавляются в пул строк.Когда JVM загружает пользовательские классы из JAR файла, все строковые литералы в этих классах также добавляются в пул строк.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15❤1🔥1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥2
Лямбда-выражения в Java представляют собой способ компактного определения и использования анонимных функций. Они были введены в Java 8 и предоставляют способ писать более лаконичный и выразительный код, особенно при работе с коллекциями и функциональными интерфейсами.
Под капотом, лямбда-выражения в Java работают благодаря следующим ключевым концепциям и механизмам:
Лямбда-выражения могут использоваться только в контексте функционального интерфейса, который является интерфейсом с одним абстрактным методом. Примеры функциональных интерфейсов:
Runnable, Callable, Comparator, и интерфейсы из пакета java.util.function (Function, Predicate, Consumer, Supplier).Лямбда-выражения компилируются в байт-код, который использует метод
invokedynamic для создания инстанций функциональных интерфейсов. Этот механизм позволяет JVM динамически генерировать реализацию функционального интерфейса, используя метод-обработчик (MethodHandle).Когда вы пишете лямбда-выражение, компилятор создает байт-код, который при выполнении использует
invokedynamic инструкцию. Это позволяет JVM динамически создать реализацию функционального интерфейса при первом вызове лямбда-выражения.List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));Компилятор преобразует лямбда-выражение в байт-код, который использует инструкцию
invokedynamic. Инструкция invokedynamic указывает на метафабрику (LambdaMetafactory), которая отвечает за создание инстанции функционального интерфейса. Метафабрика создает реализацию функционального интерфейса, используя метод-обработчик (MethodHandle) для метода, определенного в лямбда-выражении.Лямбда-выражение
name -> System.out.println(name) соответствует методу функционального интерфейса Consumer<String>. В байт-коде создается invokedynamic инструкция, которая ссылается на LambdaMetafactory для создания инстанции Consumer<String>.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥3❤1
Да, лямбда-выражения являются важной частью современной разработки на Java, и я активно их использую в своей работе. Лямбда-выражения помогают писать более лаконичный и выразительный код, особенно при работе с коллекциями и потоками данных. Вот несколько распространенных случаев использования лямбда-выражений в Java:
Использование лямбда-выражений с методом
forEach позволяет компактно и удобно итерировать по элементам коллекций. List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));С использованием Stream API и лямбда-выражений можно легко фильтровать, сортировать и преобразовывать коллекции.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");
List<String> filteredNames = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
filteredNames.forEach(System.out::println); // Вывод: AliceЛямбда-выражения упрощают сортировку коллекций с использованием метода
sort. List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.sort((name1, name2) -> name1.compareTo(name2));
names.forEach(System.out::println);Лямбда-выражения широко используются с функциональными интерфейсами, такими как
Predicate, Function, Consumer и Supplier. // Predicate
Predicate<String> startsWithA = s -> s.startsWith("A");
boolean result = startsWithA.test("Alice"); // true
// Function
Function<String, Integer> lengthFunction = s -> s.length();
int length = lengthFunction.apply("Hello"); // 5
// Consumer
Consumer<String> printConsumer = s -> System.out.println(s);
printConsumer.accept("Hello, World!"); // Вывод: Hello, World!
// Supplier
Supplier<String> stringSupplier = () -> "Hello, Supplier!";
String suppliedString = stringSupplier.get();
System.out.println(suppliedString); // Вывод: Hello, Supplier!
Лямбда-выражения с использованием параллельных потоков позволяют легко выполнять параллельные вычисления.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream()
.mapToInt(Integer::intValue)
.sum();
System.out.println("Sum: " + sum); // Вывод: Sum: 15
Предположим, у нас есть список сотрудников, и мы хотим отфильтровать и отсортировать их по имени.
import java.util.*;
import java.util.stream.Collectors;
class Employee {
private String name;
private int age;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Employee{name='" + name + "', age=" + age + '}';
}
}
public class LambdaExample {
public static void main(String[] args) {
List<Employee> employees = Arrays.asList(
new Employee("Alice", 30),
new Employee("Bob", 25),
new Employee("Charlie", 35),
new Employee("David", 28)
);
// Фильтрация и сортировка сотрудников по имени
List<Employee> filteredAndSorted = employees.stream()
.filter(e -> e.getAge() > 27)
.sorted(Comparator.comparing(Employee::getName))
.collect(Collectors.toList());
filteredAndSorted.forEach(System.out::println);
}
}
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍25🔥3
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥3🎉1
Лямбда-выражения в Java работают на основе нескольких ключевых концепций и механизмов, включая функциональные интерфейсы, инструкции
invokedynamic, и использование java.lang.invoke.MethodHandle и java.lang.invoke.LambdaMetafactory. Лямбда-выражения могут использоваться только в контексте функционального интерфейса. Функциональный интерфейс — это интерфейс, который имеет только один абстрактный метод. Примеры функциональных интерфейсов:
Runnable, Callable, Comparator, и интерфейсы из пакета java.util.function (Function, Predicate, Consumer, Supplier).invokedynamic — это инструкция байт-кода, введенная в Java 7, которая позволяет динамически связывать вызовы методов во время выполнения. В случае лямбда-выражений, invokedynamic используется для создания инстанции функционального интерфейса.MethodHandle — это легковесный, типобезопасный способ описания подлежащих вызову методов, конструкторов и полей. LambdaMetafactory — это утилита, используемая JVM для создания реализации функционального интерфейса на основе лямбда-выражения. При выполнении инструкции invokedynamic JVM вызывает LambdaMetafactory для создания экземпляра функционального интерфейса.List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));Компилятор преобразует лямбда-выражение в байт-код, который использует инструкцию
invokedynamic. Инструкция invokedynamic указывает на метод-обработчик (MethodHandle) для метода System.out.println(name).При выполнении инструкции
invokedynamic JVM вызывает LambdaMetafactory для создания инстанции функционального интерфейса Consumer<String>. LambdaMetafactory создает реализацию интерфейса Consumer<String> с методом accept, который вызывает System.out.println(name).// Лямбда-выражение
Consumer<String> consumer = name -> System.out.println(name);
// Компилируется в байт-код, который использует invokedynamic
Consumer<String> consumer = (Consumer<String>) LambdaMetafactory.metafactory(
caller,
"accept",
MethodType.methodType(Consumer.class),
MethodType.methodType(void.class, Object.class),
MethodHandles.lookup().findVirtual(System.out.getClass(), "println", MethodType.methodType(void.class, String.class)),
MethodType.methodType(void.class, String.class)
).getTarget().invoke();
Лямбда-выражения позволяют писать более компактный и читаемый код.
Использование
invokedynamic и LambdaMetafactory позволяет JVM генерировать высокоэффективный байт-код для лямбда-выражений.Лямбда-выражения могут использоваться в любых контекстах, где ожидается функциональный интерфейс.
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8🔥2❤1👀1