Прежде всего посмотрим что говорит нам Java-doc
про объекты и ссылки:
An object is a class instance or an array.
The reference values (often just references) are pointers to these objects, and a special null reference, which refers to no object.
Что значит примерно следующее: Объект - это инстанс класс или массив. Значение ссылки - это указатель на объект или специальная null
ссылка, указывающая на отсутствие объекта.
В Java
ссылки крайне важны в контексте сборки мусора, т.е при работе GC
. При работе GC
учитывает следующие типы ссылок:
strong reference
soft reference
weak reference
phantom reference
Разберем подробнее их.
Всегда, когда мы пишем new
- мы создаем strong
reference. Объект не может быть удален GC
до тех пор, пока у него есть хотя бы одна strong
ссылка.
Объекты созданные с SoftReference
будут уничтожены GC
в случае, если JVM
требуется больше памяти. Все soft references
объекты будут собраны GC
перед(до того как) JVM
кинет OutOfMemoryError
. SoftReference
используются для различных кэшей.
WeakReference
могут быть удалены GC
когда он посчитает нужным, при этом не важно, будет ли достаточно памяти или нет. Т.е когда на объект нет soft
и strong
ссылок - он может быть финализирован(finalize), удален GC
.
Небольшой пример:
public class WeakReferenceExample {
public static void main(String[] args) {
WeakReference<Integer> ref = new WeakReference<>(1000);
System.out.println("Before gc: " + ref.get());
for (int i = 0; ref.get() != null; i++) {
System.gc();
System.out.printf("After gc, iteration: %d, ref: %d", i, ref.get());
}
}
}
Что выведет нам:
Before gc: 1000
After gc, iteration: 0, ref: null
Т.е после первой просьбы к GC
объект удалили.
Объекты созданные через PhantomReference
- если на объект есть только фантомная ссылка, то будет выполнена попытка утилизации данного объекта при сборке мусора. Сам объект при этом не будет удален из памяти до тех пор, пока на него существует фантомная ссылка или данная фантомная ссылка не очищена с помощью вызова метода clear()
.
Что должна уметь ссылка?
- Присваивание.
- Доступ к полям объекта
- Вызов методов
- Приведение типа
- Конкатенация строк ('+' operator)
- Проверка на принадлежность к типу -
instanceof
. - Сравнение - '==' and '=!'
- условный оператор ? :
У каждого объекта есть счетчик строгих ссылок . Когда на объект появляется еще одна ссылка - счетчик увеличивается. Когда мы убираем ссылку с объекта или присваиваем null
- счетчик уменьшается. Если объект не имеет ссылок - он становится доступным для GC
.
Тип объекта контролируется по ссылке. Контроль происходит в compile time
.
Оператор конкатенации строк требует, чтобы хотя бы один операнд был строкой. Если это так - второй операнд конвертится к строке благодаря toString()
методу(если ссылка null
или toString()
возвращает null
- будем работать со строкой "null"
) и после этого мы создаем новый объект String с результатом конкатенации.
Оператор, который позволяет определить тип Object. Если у нас variable = null
-результат variable instance of Type
будет всегда false
. Важный момент - childObjectClass instanceof parentObjectClass
будет true
. Потому что instanceOf - это наследование is a
(является).
Если сравниваемые объекты разных типов, то один из них должен иметь возможность приведения к другому, иначе такой код не скомпилируется.
Как передаются параметры-объекты. Рассмотрим пример:
class Example {
public void methodM() {
MyExampleClass link1 = new MyExampleClass();
method(link1);
}
public void method(MyExampleClass link2) {
//2
//some work
}
class MyExampleClass {
int x;
}
}
Видим, что тут мы создаем ссылку link1
, ей присваиваем объект какой-то. Передаем ссылку в некий method
.
Теперь вопрос, а сколько у нас ссылок в //2 на объект наш?
Немного не очевидно, но их две. И вот почему.
Создается еще одна ссылка, в нее копируется область памяти первой ссылки, отсюда у нас появляется две ссылки, которые указывают на один и тот же объект. При этом, если мы в методе изменим наш объект - он изменится и во вне метода, что логично - мы через ссылку получаем объект и с ним работаем. Но! Если мы ссылке в методе присвоим какой-то другой объект, то наша первая ссылка не изменится. Т.е если я в методе link2
присвою новый объект, то link2
будет указывать на этот объект, а link1
будет все также указывать на мой старый объект.
По сути ссылка в java
- это штука, которая сама является оберткой над именно ссылкой в понимании C/C++
, надо кусочком памяти ее.
Поэтому в java
, если мы хотим написать что-то по типу swap-метода, который меняет местами два объекта - не совсем тривиально(не нужно?).
Если мы хотим написать что-то типа:
public void swap(String a, String b) {
String tmp = a;
a = b;
b = tmp
}
То, как я и говорил выше, мы поменяем местами ТОЛЬКО в методе, как только метод закончится - все вернется на круги своя. Мы никак не работаем со ссылками извне. Но что делать, если мы хотим реализовать такой метод? Все просто, надо создать объект-обертку с полем нашей строки и передавать уже этот объект, а там менять.
Если у нас final
в методе - то мы просто не можем присвоить этой ссылке другой объект, но сам объект по этой ссылке мы изменить можем. Т.е final - это гарантия того, что в методе я буду работать только с тем объектом, который передаю в вызове метода и никаким иным. Но в процессе работы - я могу изменить внутренности объекта.
//todo наверное надо переписать и про типы ссылок подробнее