Все передается по значению

Java
Все передается по значению

Только передавать по значению в Java

"В Java есть только передача по значению". Как новичок, я сначала не могу поверить в эти слова. Я бесчисленное количество раз изменял объект через функции и бесчисленное количество раз говорил своим коллегам, что при передаче объектов в Java, это передача по ссылке. После тщательного размышления я пришел к выводу, что причина, по которой я решил, что объект Java передается по ссылке, заключается в том, что многие концепции не совсем понятны, и его путают с C++. При переходе с C++ на Java правильно сопоставлять точки знаний C++ с Java, что полезно для людей, которые переходят с C++ на Java, чтобы быстрее изучить Java. Но как только карта оказывается неправильной, легко сформировать фиксированное мышление.

  Концепция ссылок есть как в C++, так и в Java, но это совсем не одно и то же. Ссылки в Java больше похожи на указатели в C++, а ссылки в C++ не имеют соответствующей концепции в Java. В C++ существует три типа передачи по значению, передаче по указателю и передаче по ссылке, но в Java нет понятия ссылки и указателя C++, и сохраняется только передача по значению.

   Чтобы лучше проиллюстрировать, что в Java существует только передача по значению, давайте сначала рассмотрим типы данных Java.Типы данных Java делятся на основные типы данных и ссылочные типы, среди которых:

  • Существует восемь основных типов, включая byte/short/int/long/float/double/char/boolean.Основной тип хранится в адресе в памяти, то есть в собственном значении, которое обычно размещается в стеке.

  • Ссылочный тип указывает на объект, который очень похож на указатель C++. Но указатели C++ могут указывать на примитивные типы и объекты класса, в то время как ссылки Java могут указывать только на объекты класса (перечисления, интерфейсы и т. д.). В Java сам объект размещается в куче, тогда как ссылочный тип размещается в стеке, а его адрес памяти хранит адрес объекта в куче. Расположение двух типов в памяти следующее:

   На приведенном выше рисунке хорошо видна разница между базовыми и ссылочными типами: данные базового типа — это сами данные, а ссылочный тип — просто ссылка. Давайте рассмотрим пример, чтобы увидеть разницу между примитивными и ссылочными типами при передаче параметров:

 1 class MyInteger {
 2     int value;
 3 }
 4 
 5 public class TestReference {
 6 
 7     public static void changeBasic(int arg) {
 8         arg = 2;
 9     }
10 
11     public static void changeReference(MyInteger arg) {
12         arg.value = 2;
13     }
14 
15     public static void main(String[] args) {
16 
17         int basicTypeA = 1;
18 
19         MyInteger referenceTypeA = new MyInteger();
20         referenceTypeA.value = 1;
21 
22         System.out.println("调用 changeBasic 之前 basicTypeA 的值 "+ basicTypeA);
23         changeBasic( basicTypeA);
24         System.out.println("调用 changeBasic 之后 basicTypeA 的值 "+ basicTypeA);
25 
26         System.out.println("调用 changeReference 之前 referenceTypeA 的值 "+ referenceTypeA.value);
27         changeReference( referenceTypeA);
28         System.out.println("调用 changeReference 之后 referenceTypeA 的值 "+ referenceTypeA.value);
29     }
30 }

Результат    следующий:

   Видно, что значение переменной basicTypeA базового типа int не изменилось после вызова changeBasic, а переменная referenceTypeA ссылочного типа MyInteger изменилась после вызова changeReference. Здесь легче ввести читателей в заблуждение, заставив их думать, что примитивные типы Java передаются по значению, а ссылочные типы передаются по ссылке (на данный момент это определение).

На самом деле это не так.Всем известен смысл передачи по значению: передается копия значения.Например, при вызове changeBasic(basicTypeA) в приведенном выше коде arg является копией basicTypeA, так что неважно что вы делаете с arg, это не повлияет на саму переменную basicTypeA. При вызове changeReference(referenceTypeA) arg также является копией referenceTypeA, но поскольку arg и referenceTypeA являются ссылочными типами и указывают на один и тот же объект, изменение объекта с помощью arg также может видеть referenceTypeA. Два типа переменных вызываются в памяти следующим образом:

   Итак, видно, что независимо от того, является ли это базовым типом или ссылочным типом, он передается по значению. Просто из-за того, что они представляют в памяти, конечные результаты отличаются. Точно так же передача по значению, по указателю и по ссылке в C++ теоретически может быть классифицирована как передача по значению (на самом деле эта классификация была обобщена при изучении C++, но позже забыли).

Ямы передачи по значению для «ссылок»

Ссылка в Java аналогична указателю в C++, но передача объекта (исключая базовые типы) в C++ предусматривает два метода прямой передачи самого объекта и передачи указателя (метод ссылки не обсуждается), в то время как объекты Java передаются только по ссылке. непосредственная передача самого объекта.

  • Преимущество передачи самого объекта заключается в том, что передается копия объекта, и любая модификация скопированного объекта в функции не изменит исходный объект. Но если переданный объект очень большой, а вызов очень частый, это повлияет на производительность.
  • Преимущество передачи ссылки (или указателя) объекта состоит в том, что необходимо скопировать только данные размера одной ссылки (или указателя), а содержимое исходного объекта можно изменить в вызываемой функции. Недостаток в том, что легко копать ямы.

В C++ два вышеуказанных метода передачи можно выбрать самостоятельно, а в Java есть только второй способ.Все имеет свои преимущества и недостатки.Иногда мы не хотим менять содержимое исходного объекта в функции.Здесь я наступил на яму, наш процесс прохождения объекта по конвейеру в проекте выглядит следующим образом:

За funcA и funcB отвечают два разных человека.После апгрейда ответственный за funcB обнаружил, что содержимое объекта X, полученного в функции, было неправильным.Сначала он подумал, что произошла ошибка в передаче интерфейса объект X. Наконец, было обнаружено, что объект X был изменен в funcA после обновления, на что было потрачено много времени. Конечно, неразумный процесс проектирования этой архитектуры является основной причиной (описанных выше проблем можно избежать, вручную копируя объект X при раздаче), но это не мешает нам отбросить недостатки, заключающиеся в том, что Java может передавать только ссылки.

   Вышеперечисленные ошибки легко сделать в длинной цепочке вызовов и различных циклах for/while.Решение, конечно, состоит в том, чтобы скопировать объект вручную.В Java есть два способа скопировать объект:

  • Реализуйте интерфейс Cloneable и переопределите метод clone() в классе Object.
  • Внедрение интерфейса Serializable и реализация клонирования посредством сериализации и десериализации объектов может обеспечить настоящее глубокое клонирование.

   Второй метод позволяет избежать проблемы глубокого и поверхностного копирования, но его вызов занимает много времени. В первом тоже можно избежать глубокого и мелкого копирования, но нужно вручную писать соответствующий код самостоятельно, если вложенность глубокая, то код будет очень сложным. Что касается проблемы глубокого и мелкого копирования, то Baidu можно сделать самостоятельно, но суть все же в некоторых проблемах, вызванных простой передачей ссылки на объект. Не забудьте обратить внимание на официальный аккаунт, в котором записан путь обучения программиста C++ Java.