Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
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
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍22🔥5
В одном лямбда-выражении можно определить и реализовать только одну функциональность, поскольку лямбда-выражение предназначено для реализации одного абстрактного метода функционального интерфейса. Функциональный интерфейс — это интерфейс, который содержит только один абстрактный метод.
Consumer — это функциональный интерфейс, который принимает один аргумент и не возвращает результата. List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// Лямбда-выражение для Consumer
names.forEach(name -> System.out.println(name));Function — это функциональный интерфейс, который принимает один аргумент и возвращает результат. Function<String, Integer> lengthFunction = str -> str.length();
int length = lengthFunction.apply("Hello");
System.out.println("Length: " + length); // Вывод: Length: 5
Predicate — это функциональный интерфейс, который принимает один аргумент и возвращает логическое значение. Predicate<String> startsWithA = str -> str.startsWith("A");
boolean result = startsWithA.test("Alice");
System.out.println("Starts with A: " + result); // Вывод: Starts with A: trueХотя одно лямбда-выражение предназначено для реализации одной функциональности, вы можете включить в него более сложную логику, используя блоки кода
{}. Predicate<String> complexPredicate = str -> {
if (str == null || str.isEmpty()) {
return false;
}
return str.startsWith("A") && str.length() > 3;
};
boolean result = complexPredicate.test("Alice");
System.out.println("Complex Predicate: " + result); // Вывод: Complex Predicate: trueЕсли нужно выполнить несколько различных функциональностей, можно комбинировать несколько лямбда-выражений или цепочку вызовов.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Alex");
// Фильтрация, преобразование и итерация
names.stream()
.filter(name -> name.startsWith("A")) // Predicate
.map(name -> name.toUpperCase()) // Function
.forEach(name -> System.out.println(name)); // ConsumerВ некоторых случаях вы можете встретить вложенные лямбда-выражения, особенно при работе с функциями высшего порядка.
BiFunction<Integer, Integer, Integer> add = (a, b) -> a + b;
Function<Integer, Function<Integer, Integer>> addPartial = a -> b -> add.apply(a, b);
Function<Integer, Integer> add5 = addPartial.apply(5);
int result = add5.apply(3); // 5 + 3 = 8
System.out.println("Result: " + result); // Вывод: Result: 8
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥4❤1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍22🔥1
Методы
equals и hashCode являются методами класса java.lang.Object в Java. Все классы в Java, неявно или явно, наследуются от класса Object, поэтому они унаследуют эти методы.Класс
Object является корневым классом в иерархии классов Java. Каждый класс в Java наследуется от этого класса, либо прямо, либо через цепочку других классов.Метод
equals используется для сравнения объектов на равенство. Метод equals должен следовать следующим правилам: Симметричность: Для любых ненулевых значений x и y, x.equals(y) должно возвращать true, если и только если y.equals(x) возвращает true.
Транзитивность: Для любых ненулевых значений x, y и z, если x.equals(y) возвращает true и y.equals(z) возвращает true, то x.equals(z) должно возвращать true.
Согласованность: Для любых ненулевых значений x и y, повторные вызовы x.equals(y) должны возвращать одно и то же значение, пока объекты остаются неизменными.
Сравнение с null: Для любого ненулевого значения x, x.equals(null) должно возвращать false.
Метод
hashCode возвращает хеш-код объекта, который используется для повышения производительности в структурах данных, таких как HashMap, HashSet и Hashtable.Подразумеваемый контракт: Метод hashCode должен следовать следующим правилам:
Согласованность: Если объект не изменяется, повторные вызовы метода hashCode должны возвращать одно и то же значение.
Согласованность с equals: Если два объекта равны согласно методу equals, их хеш-коды также должны быть равны.
Неравенство: Если два объекта не равны согласно методу equals, их хеш-коды не обязательно должны быть различными, но желательно минимизировать количество таких коллизий.
Чтобы обеспечить правильное поведение этих методов в пользовательских классах, их часто переопределяют.
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Person person = (Person) obj;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
public static void main(String[] args) {
Person person1 = new Person("Alice", 25);
Person person2 = new Person("Alice", 25);
System.out.println(person1.equals(person2)); // true
System.out.println(person1.hashCode() == person2.hashCode()); // true
}
}Современные IDE, такие как IntelliJ IDEA и Eclipse, могут автоматически генерировать методы
equals и hashCode на основе полей класса. Это помогает избежать ошибок и обеспечить правильное соблюдение контрактов.Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍13❤1
Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍22🔥3
Хеш-коды могут быть равны для разных объектов из-за того, что они являются конечными представлениями данных, а количество возможных хеш-кодов ограничено. В Java метод
hashCode возвращает значение типа int, что означает, что существует только \(2^{32}\) возможных значений хеш-кодов. Однако количество возможных объектов значительно больше, чем \(2^{32}\), что приводит к коллизиям хеш-кодов.Хеш-коды представлены 32-битным целым числом, что дает \(2^{32}\) возможных значений. Это означает, что множество объектов должно быть отображено в это ограниченное пространство хеш-кодов, что приводит к коллизиям.
Коллизия возникает, когда два разных объекта имеют одинаковый хеш-код. Это неизбежно при использовании конечного диапазона хеш-кодов для представления множества объектов.
В Java структуры данных, такие как
HashMap и HashSet, обрабатывают коллизии хеш-кодов, используя внутренние механизмы для разрешения коллизий. При коллизии хеш-кодов, когда два разных объекта имеют одинаковый хеш-код, они все равно могут быть правильно размещены и найдены в хеш-таблице.public class Example {
private int value;
public Example(int value) {
this.value = value;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
Example example = (Example) obj;
return value == example.value;
}
@Override
public int hashCode() {
return value % 10; // Искусственно создаем коллизии для демонстрации
}
public static void main(String[] args) {
Example ex1 = new Example(1);
Example ex2 = new Example(11);
System.out.println(ex1.hashCode()); // 1
System.out.println(ex2.hashCode()); // 1
System.out.println(ex1.equals(ex2)); // false
HashSet<Example> set = new HashSet<>();
set.add(ex1);
set.add(ex2);
System.out.println(set.size()); // 2, так как объекты разные
}
}Каждый элемент в хеш-таблице указывает на список (или другую структуру), содержащий все элементы с одинаковым хеш-кодом.
В случае коллизии алгоритм ищет другую позицию в хеш-таблице для размещения элемента, используя определенную стратегию пробирования (linear probing, quadratic probing и т.д.).
Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
👍10❤1
Anonymous Quiz
65%
a) substring()
24%
b) subString()
8%
c) getSubstring()
3%
d) extract()
👾6👍5🤔2