Эта статья призвана рассказать самые скучные базовые знания на самом популярном языке.
Любой, кто изучил основы Java, знает: передача по значению и передача по ссылке представляют собой трудность при первом знакомстве с Java. Иногда вы помните синтаксис, но не помните, как его использовать на самом деле. Иногда вы можете использовать его, но вы не можете объяснить принцип. Тема полна противоречий: в некоторых сообщениях на форуме говорится, что в Java есть только передача по значению, а в некоторых блогах говорится, что в ней есть и то, и другое; это немного сбивает с толку, давайте сделайте некоторое обсуждение на эту тему, для книг, блогов форума Сделайте заявление и сделайте исследование, чтобы получить заслуживающий доверия ответ.
На самом деле, для синтаксиса и применения передачи по значению и по ссылке вы можете получить значительное количество объяснений и примеров, посмотрев на Baidu. взять интервью и задать письменный контрольный вопрос об этом пункте знаний, я чувствую, что могу, напишите ответ со зрелостью, но узнайте, что это неправильно, или что вы просто не будете этого делать.
какова причина?
Это потому, что у вас нет полного понимания точки знания, вы знаете только ее кожу. Ознакомиться с грамматикой очень просто, и понять строчку кода несложно, но очень сложно интегрировать полученные знания и последовательно их понимать. ,начиная с модели памяти,шаг за шагом вводятся основные принципы передачи значений и передачи ссылок.Поэтому длина больше и точек знаний больше.Надеюсь читатели будут более терпимы.
1. Форма участия в фактических параметрах
Давайте сначала рассмотрим набор синтаксисов:
Формальный параметр: Параметр, который необходимо передать при вызове метода, например: a in func(int a), он имеет смысл только при вызове func, то есть место в памяти будет выделено после выполнения method func завершен, a будет уничтожен для освобождения места, то есть фактических параметров не будет: фактическое значение, переданное при вызове метода, оно было инициализировано до вызова метода и передано при вызове метода. называется.
Возьмите каштан:
1publicstaticvoidfunc(int a){2 a=20;3 System.out.println(a);4}5publicstaticvoidmain(String[] args) {6int a=10;//实参7 func(a);8}
В примере a int a=10; был создан и инициализирован перед вызовом.При вызове метода func он передается как параметр, так что это a является фактическим параметром. a в func(int a) начинает свой жизненный цикл только тогда, когда вызывается func, и после завершения вызова func также освобождается JVM, поэтому это a является формальным параметром.
2. Типы данных Java
Так называемый тип данных — это абстрактное выражение памяти в языках программирования. Мы знаем, что программа состоит из файлов кода и статических ресурсов. До запуска программы эти коды существуют на жестком диске. Когда программа начинает работать , эти коды будут преобразовывать содержимое, которое может распознать компьютер, в память и выполнять его. следовательно
Типы данных в основном используются для определения формы хранения данных того же типа в языке программирования, то есть определения того, как биты, представляющие эти значения, хранятся в памяти компьютера.
Следовательно, хранение данных в памяти основано на типе данных, чтобы определить форму хранения и место хранения. Итак, какие типы данных существуют в Java?
Примитивные типы: наименьшие гранулярные типы данных, встроенные в язык программирования. Он включает в себя четыре категории и восемь типов: 4 целочисленных типа: byte, short, int, long2 типа с плавающей запятой: float, double1 символьный тип: char1 логический тип: логический тип ссылки: ссылка также называется дескриптором, типом ссылки, это данные форма, определенная на языке программирования, которая хранит значение адреса, где фактический контент находится в дескрипторе. В основном он включает в себя: массив интерфейсов классов
С типами данных управление программными данными в JVM стандартизировано.Различные типы данных имеют разные формы и места хранения.Если вы хотите знать, как JVM хранит различные типы данных, вы должны сначала понять JVM.Память разделена и функции каждого часть.
3. Разделение и функции памяти JVM
Сам язык Java не может оперировать памятью.Все в нем управляется и контролируется JVM.Поэтому деление области памяти Java также является делением области JVM.Прежде чем говорить о делении памяти JVM,возьмем Взгляните на Java.Процесс выполнения программы выглядит следующим образом:
Боюсь 1-solve.byte IMG.com/to S-talent-i-he 2…
Из рисунка видно, что после того, как код Java компилируется компилятором в байт-код, JVM открывает пространство памяти (также называемое областью данных времени выполнения), которое добавляется в область данных времени выполнения загрузчиком классов для хранения данные, которые необходимо использовать во время выполнения программы.В этой области данных она состоит из следующих частей:
1. Стек виртуальных машин
2. Куча
3. Счетчик программ
4. Область метода
5. Стек собственных методов
Давайте рассмотрим принцип работы каждой части и какие данные используются для хранения процесса выполнения программы.
- стек виртуальных машин
Стек виртуальной машины представляет собой модель памяти для выполнения метода Java. Кадры стека хранятся в стеке. Каждый кадр стека соответствует вызываемому методу. Процесс вызова метода соответствует процессу укладки кадра стека в виртуальную память. машина.
Стек является потоко-приватным, то есть стеки между потоками изолированы, когда поток в программе начинает выполнять метод, создается кадр стека и помещается в стек (на вершину стека). метод завершается, кадр стека выталкивается.
На следующем рисунке показана модель стека Java и состав кадра стека:
[data:image/svg+xml;utf8,](data:image/svg+xml;utf8,)
кадр стека: это структура данных, используемая для поддержки виртуальной машины при вызове и выполнении методов, и это элемент стека стека виртуальной машины в области данных времени выполнения виртуальной машины.
Каждый кадр стека включает в себя:
- таблица локальных переменных: Используется для хранения локальных переменных в методах (нестатические переменные, параметры функций). Когда переменная является базовым типом данных, значение сохраняется напрямую, а когда переменная является ссылочным типом, сохраняется ссылка на конкретный объект.
- стек операндов: механизм выполнения интерпретации виртуальной машины Java называется «механизм выполнения на основе стека», а упомянутый стек является стеком операндов.
- ссылка на пул констант времени выполнения: Хранит ссылки на константы, которые могут использоваться во время выполнения программы.
- адрес возврата метода: Сохраняет адрес возврата после завершения выполнения метода.
- куча:
Куча используется для хранения самого объекта и массива.В JVM есть только одна куча, поэтому куча используется всеми потоками.
- Область метода:
Область методов — это логическая область памяти, совместно используемая всеми потоками.В JVM есть только одна область методов, которая используется для хранения некоторого содержимого, доступного для потоков.Это потокобезопасно.Когда несколько потоков получают доступ к одному и тому же содержимому. в области методов одновременно может быть только один поток, загружающий данные, другие потоки могут только ждать.
Содержимое, которое может быть сохранено в области метода: полное имя пути класса, полное имя прямого суперкласса класса, модификатор доступа класса, тип класса (класс или интерфейс), и порядок полного имени прямого интерфейса класса.Списки, константные пулы (поля, информация о методе, статические переменные, ссылки на типы (класс)) и т.д.
- Стек собственных методов:
Функция собственного стека методов в основном такая же, как у стека виртуальной машины, и он также является потоком.Разница между ними заключается в том, что стек виртуальной машины служит для выполнения методов Java, а собственный стек методов служит для выполнение нативных методов.
Можно задаться вопросом: что такое нативный метод? Почему Java до сих пор вызывает нативные методы?
- Счетчик команд:
Тема закрытая. Записывает индикатор номера строки байт-кода, выполняемого текущим потоком.Во время выполнения программы интерпретатор байт-кода выбирает следующую инструкцию байт-кода для выполнения путем изменения значения этого счетчика, ветвления, основных функций, таких как цикл, исключение обработка и восстановление потоков должны выполняться с использованием счетчиков.
4. Как данные хранятся в памяти?
Из приведенной выше диаграммы работы программы видно, что JVM выделяет память в трех местах во время работы программы:
- куча
- куча
- область статического метода
- постоянная площадь
Соответственно, каждая область хранения имеет свою стратегию выделения памяти:
- Куча:
- куча
- статический
Мы уже знаем: типы данных в Java имеют базовые типы данных и ссылочные типы данных, так какая же стратегия используется для хранения этих данных? Вот следующие ситуации для расследования:
- Хранение базовых типов данных: A. Локальные переменные базовых типов данных B. Переменные-члены базовых типов данных C. Статические переменные базовых типов данных 2. Хранение справочных типов данных
- Хранение основных типов данных
Изучим отдельно:
A. Локальные переменные примитивных типов данных
-
Локальные переменные и данные, определяющие базовый тип данных, хранятся непосредственно в стеке в памяти, которая представляет собой «стек виртуальной машины», упомянутый ранее, а значение самих данных хранится в пространстве стека.
[data:image/svg+xml;utf8,](data:image/svg+xml;utf8,)
Как показано на рисунке выше, переменные, определенные в методе, хранятся непосредственно в стеке, например
1int age=50;2int weight=50;3int grade=6;
Когда мы пишем «int age=50;», это фактически делится на два шага:
1int age;//定义变量2age=50;//赋值
Сначала JVM создает переменную с именем age, которая хранится в таблице локальных переменных, а затем обращается к стеку, чтобы выяснить, существует ли литеральное значение 50. Если да, то она напрямую указывает age на этот адрес. вверх пробел для хранения содержимого «50» и укажите возраст на этот адрес. Итак, мы можем знать:Когда мы объявляем и инициализируем локальные переменные примитивных типов данных, имена переменных и литеральные значения сохраняются в стеке и являются реальным содержимым.
Давайте снова посмотрим на "int weight=50;" согласно идее только что: контент с литеральным значением 50 уже существует в стеке, поэтому вес напрямую указывает на этот адрес. Видно, что:Данные в стеке совместно используются текущим потоком.
А что, если следующий код будет выполнен снова?
1weight=40;
Когда весовая переменная переназначается в коде, JVM перейдет к стеку, чтобы найти содержимое с литеральным значением 40. Если она не найдет значения, она откроет пространство памяти для хранения содержимого 40, и указать вес на этот адрес. Отсюда видно, что:
Сами данные базового типа данных не изменятся.При переназначении локальной переменной она не меняет литеральное содержимое в памяти, а осуществляет повторный поиск существующих таких же данных в стеке.Если в стеке его нет , затем повторно откройте память для хранения новых данных и укажите ссылку на локальную переменную, которую необходимо переназначить, на адрес новых данных.
B. Переменные-члены примитивных типов данных
Переменная-член: как следует из названия, это переменная, определенная в теле класса. Посмотрите на картинку ниже:
[data:image/svg+xml;utf8,](data:image/svg+xml;utf8,)
Видим, что адрес per указывает на область в куче памяти, восстановим код:
1publicclassPerson{ 2privateint age; 3private String name; 4privateint grade; 5//篇幅较长,省略setter getter方法 6 static void run(){ 7 System.out.println("run...."); 8 }; 9}1011//调用12Person per=new Person();
Возраст, имя и класс одних и тех же локальных переменных хранятся в пространстве кучи для каждого объекта. Поэтому видно, что:Имена переменных-членов и значения базовых типов данных хранятся в куче, и их жизненный цикл соответствует циклу объекта.
C. Статические переменные примитивных типов данных
упомянутый ранееОбласть методаиспользуется для хранения некоторых общих данных, поэтомуИмена статических переменных и значения базовых типов данных хранятся в пуле констант времени выполнения области методов.Статические переменные загружаются при загрузке класса и исчезают при исчезновении класса.
- Хранение справочных типов данных:
Как упоминалось выше: куча используется для хранения самого объекта и массива, а ссылка (дескриптор) хранит значение адреса фактического содержимого, поэтому на приведенной выше диаграмме работы программы также видно, что когда мы определяем объект
1Person per=new Person();
На самом деле, у него также есть два процесса:
1Person per;//定义变量2per=new Person();//赋值
При выполнении Person per; JVM сначала открывает часть памяти в таблице переменных в стеке виртуальной машины для хранения переменной per При выполнении per=new Person() JVM создает экземпляр объекта класса Person и открывает блок в куче. Этот экземпляр хранится в памяти, а значение адреса экземпляра присваивается каждой переменной. Итак, можно увидеть:Для объектов/массивов ссылочных типов данных имя переменной хранится в стеке, а значение переменной хранит адрес объекта, а не фактическое содержимое объекта.
6. Передача по значению и по ссылке
Ранее были введены формальные параметры и фактические параметры, а также типы данных и форма хранения данных в памяти, далее тема статьи: передача по значению и передача по ссылке.
Передача по значению: при вызове метода фактический параметр передает копию своего содержимого в метод через формальный параметр.В это время содержимое, полученное формальным параметром, является копией фактического значения параметра, поэтому любая операция над формальным параметром в методе, являются просто операциями над этой копией и не влияют на содержимое исходного значения.
Давайте посмотрим пример:
1publicstaticvoidvalueCrossTest(int age,float weight){ 2 System.out.println("传入的age:"+age); 3 System.out.println("传入的weight:"+weight); 4 age=33; 5 weight=89.5f; 6 System.out.println("方法内重新赋值后的age:"+age); 7 System.out.println("方法内重新赋值后的weight:"+weight); 8 } 910//测试11public static void main(String[] args) {12 int a=25;13 float w=77.5f;14 valueCrossTest(a,w);15 System.out.println("方法执行后的age:"+a);16 System.out.println("方法执行后的weight:"+w);17}
Выходной результат:
1传入的age:252传入的weight:77.534方法内重新赋值后的age:335方法内重新赋值后的weight:89.567方法执行后的age:258方法执行后的weight:77.5
Как видно из приведенных выше результатов печати: после того, как a и w передаются в valueCrossTest в качестве фактических параметров, независимо от того, какая операция выполняется в методе, в конечном итоге a и w остаются неизменными.
Что это за форма? ! !
Ниже приводится подробный анализ, основанный на знаниях, полученных выше:
Во-первых, когда программа работает, вызывается метод main().В это время JVM помещает фрейм стека в стек виртуальной машины для метода main(), который является текущим фреймом стека, который используется для хранения таблица локальных переменных (включая параметры) в main(). ), стек операций, выход из метода и другая информация, например, a и w являются локальными переменными в методе mian(), поэтому можно сделать вывод, что a и w лежат в кадре стека, где находится метод mian, как показано на рисунке:
[data:image/svg+xml;utf8,](data:image/svg+xml;utf8,)
Когда метод valueCrossTest() выполняется, JVM также помещает стек в стек виртуальной машины для него, который является текущим кадром стека, который используется для хранения информации, такой как локальные переменные в valueCrossTest(), поэтому возраст и вес В кадре стека, где находится метод valueCrossTest, а их значения получаются копированием копии значений a и w, как показано на рисунке:
[data:image/svg+xml;utf8,](data:image/svg+xml;utf8,)
. Следовательно, содержимое, соответствующее a и age, w и весу, несовместимо, поэтому при переназначении в методе фактический процесс выглядит следующим образом:
[data:image/svg+xml;utf8,](data:image/svg+xml;utf8,)
То есть изменения возраста и веса изменяют только содержимое текущего фрейма стека (фрейма стека, где находится метод valueCrossTest), когда выполнение метода завершится, эти локальные переменные будут уничтожены, а фрейм стека, в котором метод mian будет возвращен на вершину стека. , станет текущим кадром стека, и когда a и w будут выведены снова, он по-прежнему будет содержимым инициализации. следовательно:При передаче значения передается копия реального содержания.Операция копирования не влияет на исходное содержание, то есть то, как изменяется формальный параметр, не повлияет на содержимое, соответствующее фактическому параметру.
Передача по ссылке: «ссылка» — это значение адреса, указывающее на реальное содержимое. При вызове метода адрес фактического параметра передается соответствующему формальному параметру посредством вызова метода. В теле метода формальный параметр и фактический параметр указывает на тот же адрес памяти Реальное содержимое, на которое влияют операции над формальными параметрами.
Например: сначала определите объект:
1publicclassPerson { 2private String name; 3privateint age; 4 5public StringgetName() { 6return name; 7 } 8publicvoidsetName(String name) { 9this.name = name;10 }11publicintgetAge() {12return age;13 }14publicvoidsetAge(int age) {15this.age = age;16 }17}
Напишем функцию для проверки:
1publicstaticvoidPersonCrossTest(Person person){ 2 System.out.println("传入的person的name:"+person.getName()); 3 person.setName("我是张小龙"); 4 System.out.println("方法内重新赋值后的name:"+person.getName()); 5 } 6//测试 7public static void main(String[] args) { 8 Person p=new Person(); 9 p.setName("我是马化腾");10 p.setAge(45);11 PersonCrossTest(p);12 System.out.println("方法执行后的name:"+p.getName());13}
Выходной результат:
1传入的person的name:我是马化腾2方法内重新赋值后的name:我是张小龙3方法执行后的name:我是张小龙
Видно, что содержимое человека меняется после выполнения метода personCrossTest(), что подтверждает упомянутый выше **"переход по ссылке"**, а работа с формальным параметром изменяет содержимое фактического объекта.
Итак, это конец дела? Нет, не все так просто, вы можете увидеть желаемый эффект, потому что вы просто выбрали правильный пример! ! !
Давайте немного изменим приведенный выше пример и добавим строку кода,
1publicstaticvoidPersonCrossTest(Person person){2 System.out.println("传入的person的name:"+person.getName());3 person=new Person();//加多此行代码4 person.setName("我是张小龙");5 System.out.println("方法内重新赋值后的name:"+person.getName());6 }
Выходной результат:
1传入的person的name:我是马化腾2方法内重新赋值后的name:我是张小龙3方法执行后的name:我是马化腾
`Почему вывод на этот раз отличается от предыдущего? Видишь, что не так?
В соответствии с упомянутой выше моделью памяти JVM вы можете знать, что объекты и массивы хранятся в области кучи Java, а область кучи является общей, поэтому, когда программа выполняет следующий код в методе main()
1Person p=new Person();2 p.setName("我是马化腾");3 p.setAge(45);4 PersonCrossTest(p);
JVM откроет часть памяти в куче для хранения всего содержимого объекта p и в то же время создаст ссылку на реальный адрес объекта p в области кучи хранения p в области стека поток, в котором находится метод main(), как показано на рисунке:
Боюсь 1-solve.byte IMG.com/to S-talent-i-he 2…
При выполнении метода PersonCrossTest(), потому что в методе есть такая строчка кода:
1person=new Person();
JVM нужно открыть еще один кусок памяти в куче для хранения new Person().Если адрес "xo3333", то формальный параметр person указывает на этот адрес.Если он действительно передается по ссылке, то как было сказано выше :При передаче по ссылке фактический параметр формального параметра указывает на тот же объект, и операция формального параметра изменит изменение объекта фактического параметра..
Можно сделать вывод, что фактический параметр также должен указывать на адрес только что созданного объекта person, поэтому после выполнения PersonCrossTest() окончательным выводом должно быть содержимое объекта, созданного позже.
Однако на самом деле окончательный вывод отличается от того, что мы предполагали, и окончательный вывод по-прежнему является содержимым объекта, созданного в начале.
Видно, что:Передача по ссылке не существует в Java.
Но некоторые люди могут спросить: почему в первом примере изменение содержимого формальных параметров в методе приведет к изменению содержимого исходного объекта?
Это потому что:Будь то базовый тип или ссылочный тип, когда фактический параметр передается в формальный параметр, он передается по значению, то есть передается копия, а не само содержимое.
Боюсь 1-solve.byte IMG.com/to S-talent-i-he 2…
Из рисунка видно, что формальный параметр person в методе не имеет существенного отношения к фактическому параметру p, он просто копирует адрес, указывающий на объект, из p.
И p, и person указывают на один и тот же объект.
Поэтому в первом примере операция над формальным параметром p повлияет на содержимое объекта, соответствующее фактическому параметру. Во втором примере, когда выполняется new Person(), JVM открывает пространство в куче для хранения нового объекта и изменяет человека на адрес нового объекта.
p по-прежнему указывает на старый объект, а person указывает на адрес нового объекта.
Итак, в это времяОперация человека на самом деле является операцией нового объекта, которая не имеет ничего общего с соответствующим объектом в фактическом параметре p..
Эпилог
Таким образом, можно видеть, что все параметры, передаваемые в Java, независимо от базового типа или ссылочного типа, передаются по значению или путем копирования. Только во время прохождения:
Если операция выполняется над данными базового типа данных, поскольку исходное содержимое и копия хранят фактическое значение и находятся в разных областях стека, действие формального параметра не влияет на исходное содержимое.
При работе с данными ссылочного типа возможны два случая: во-первых, формальный параметр и фактический параметр продолжают указывать на один и тот же адрес объекта, и операция формального параметра повлияет на содержимое указанного объекта. по фактическому параметру. Во-первых, формальный параметр изменяется, чтобы указать на новый адрес объекта (например, переназначение ссылки), тогда операция формального параметра не повлияет на содержимое объекта, на который указывает фактический параметр.
Вышеизложенное - размышления и аргументы редактора по вопросу "передача по значению и передача по ссылке". По этому вопросу всегда было много споров. Надеюсь обсудить и узнать с читателями. Если есть разные мнения или предложения , пожалуйста, покиньте Xiaobian WeChat: sisi -ceo. Рациональные комментарии, не распыляйте, если вам это не нравится.
Думаете, эта статья была вам полезна? Пожалуйста, поделитесь им с большим количеством людей и обратите внимание на «Неограниченное программирование», чтобы улучшить свои принудительные навыки.