Как устроены ссылки в Java?
Попробовав Rust после C++, я узнал, что там есть огромное преимущество под названием "заимствование". Эта особенность не даёт скомпилировать примерно такой код:
#include <string>
#include <vector>
#include <iostream>
int main() {
std::vector<std::string> vec{"Volvo", "BMW"};
std::string& ref = vec[0];
vec.push_back("Mercedes");
std::cout << ref << std::endl;
return 0;
}
Такой код в C++ приводит к неопределённому поведению (undefined behavior). Недавно я задался вопросом, а как с этим в Java и решил скомпилировать и запустить аналогичный код:
import java.util.ArrayList;
public class Main {
public static void main(String args[]) {
ArrayList<String> list = new ArrayList<String>();
list.add("Volvo");
list.add("BMW");
String first = list.get(0);
list.add("Mercedes");
System.out.println(first);
}
}
Как и ожидалось, в Java ссылка на первый элемент массива последовала за массивом (как я понимаю, здесь, также как в C++, массив изменил своё расположение после вызовов add) и продолжила "ссылаться" на нужный элемент.
Почему в Java такое:
- компилируется
- ссылка продолжает ссылаться на тот же элемент массива ?
Ответы (1 шт):
На Java невозможно сделать аналогичный код, это типобезопасный язык, без указателей на "сырую память".
Ваш код для Java почти аналогичен коду C++:
#include <string>
#include <vector>
#include <iostream>
int main() {
std::vector<std::string> vec;
vec.push_back("Volvo");
vec.push_back("BMW");
std::string first = vec[0];
vec.push_back("Mercedes");
std::cout << first << std::endl;
return 0;
}
Что бы восполнить недостаток функционала из за отсутствия указателей, в Java есть мягкие, слабые и фантомные ссылки.
В C++ менеджер памяти может добавить новое значение, если в выделенном alloc'е памяти хватает, если памяти не хватает менеджер памяти выделит новый alloc, и сформирует новый массив с добавленным значением.
В Java такое неопределенное поведение не получиться создать, так как массив String[] не позволяет добавлять новые значения, а список не переместит существующие значения в новую память. Но можем имитировать оба варианта:
import java.lang.ref.WeakReference;
import java.lang.StringBuilder;
import java.util.ArrayList;
public class MyClass {
public static void main(String args[]) {
ArrayList<String> list = new ArrayList<>();
list.add(new StringBuilder("Volvo").toString());
list.add(new StringBuilder("BMW").toString());
WeakReference first = new WeakReference(list.get(0));
System.out.println(first.get()); //Вернет "Volvo"
//Имитируем первый вариант (памяти в alloc'е хватает):
{
list.add(new StringBuilder("Mercedes").toString());
System.gc();
System.out.println(first.get());//Вернет "Volvo"
}
//Имитируем второй вариант (памяти в alloc'е не хватает):
{
list = new ArrayList<>();
list.add(new StringBuilder("Volvo").toString());
list.add(new StringBuilder("BMW").toString());
list.add(new StringBuilder("Mercedes").toString());
System.gc();
System.out.println(first.get()); //Вернет null
}
}
}