Недавно в разделе
Новичкам сложнее ответить правильно на этот вопрос. Когда я на следующий день разбирался с ответом, то обнаружил, что не могу описать этот вопрос простым языком и легко его понять. понять манеру. Итак, у меня есть первоначальный замысел написания этой статьи. В этой статье я начну с того, что такое фактические и формальные параметры метода, и покажу вам, почему в Java используется только передача по значению.
Время опровергать слухи
По этому вопросу вStackOverflowЭто также вызвало обширные дискуссии по этому поводу.Похоже, что многие программисты по-разному понимают этот вопрос, и даже многие люди имеют неправильное понимание. Другие могут знать, что передача параметров в Java осуществляется по значению, но не могут объяснить почему.
Прежде чем мы начнем подробно объяснять, необходимо исправить те заблуждения, которые у всех есть в прошлом. Если у вас есть следующие мысли, то вам необходимо прочитать эту статью.
Непонимание 1: передача по значению и передача по ссылке, отличительным условием является передаваемое содержимое, если это значение, то передача по значению. Если это ссылка, то она передается по ссылке.
Непонимание 2: Java передается по ссылке.
Непонимание 3: Если переданный параметр является общим типом, он передается по значению, а если это объект, он передается по ссылке.
фактический параметр участия
Все мы знаем, что параметры могут быть определены при определении метода в Java. Например, основной метод в Java,public static void main(String[] args)
, где args — параметр. В языке программирования параметры делятся на формальные параметры и фактические параметры.
Формальные параметры: параметры, используемые при определении имени функции и тела функции, используются для получения параметров, передаваемых при вызове функции.
Фактические параметры: при вызове функции с параметрами существует отношение передачи данных между вызывающей функцией и вызываемой функцией. При вызове функции в вызывающей функции параметры в круглых скобках после имени функции называются «фактические параметры».
Простой пример:
public static void main(String[] args) {
ParamTest pt = new ParamTest();
pt.sout("Hollis");//实际参数为 Hollis
}
public void sout(String name) { //形式参数为 name
System.out.println(name);
}
Фактический параметр — это то, что фактически передается при вызове параметризованного метода, а формальный параметр — это параметр, используемый для получения содержимого фактического параметра.
передавать по значению против передачи по ссылке
Как упоминалось выше, когда мы вызываем функцию с параметрами, мы передаем фактические параметры формальным параметрам. Однако в процедурных языках есть два случая передачи в этом процессе передачи, а именно передача по значению и передача по ссылке. Давайте посмотрим, как передача по значению и передача по ссылке определяются и различаются в языках программирования.
Передача по значению относится к копированию фактических параметров в функцию при вызове функции, поэтому, если параметры будут изменены в функции, фактические параметры не будут затронуты.
Передача по ссылке означает, что адрес фактического параметра напрямую передается функции при вызове функции, после чего модификация параметра в функции повлияет на фактический параметр.
Используя приведенные выше концепции, вы можете написать код для практики, давайте посмотрим, является ли он передачей по значению или передачей по ссылке в Java, поэтому получается самый простой фрагмент кода:
public static void main(String[] args) {
ParamTest pt = new ParamTest();
int i = 10;
pt.pass(10);
System.out.println("print in main , i is " + i);
}
public void pass(int j) {
j = 20;
System.out.println("print in pass , j is " + j);
}
В приведенном выше коде мы изменяем значение параметра j в методе передачи, а затем печатаем значение параметра в методе передачи и основном методе соответственно. Результат выглядит следующим образом:
print in pass , j is 20
print in main , i is 10
Можно видеть, что изменение значения имени внутри метода pass не меняет значение фактического параметра i. Затем, в соответствии с приведенным выше определением, кто-то пришел к выводу: передача методов в Java — это передача значений.
Однако вскоре это было поставлено под сомнение (ха-ха, так что не торопитесь с выводами). Затем они переместят следующий код:
public static void main(String[] args) {
ParamTest pt = new ParamTest();
User hollis = new User();
hollis.setName("Hollis");
hollis.setGender("Male");
pt.pass(hollis);
System.out.println("print in main , user is " + hollis);
}
public void pass(User user) {
user.setName("hollischuang");
System.out.println("print in pass , user is " + user);
}
Это также метод передачи, который также изменяет значение параметра в методе передачи. Результат выглядит следующим образом:
print in pass , user is User{name='hollischuang', gender='Male'}
print in main , user is User{name='hollischuang', gender='Male'}
После выполнения метода pass значение фактического параметра было изменено.Согласно определению передачи по ссылке выше, значение фактического параметра было изменено.Разве это не передача по ссылке? Итак, на основе приведенных выше двух кусков кода кто-то пришел к новому выводу: в Java-методах при передаче общих типов он передается по значению, а при передаче типов объектов — по ссылке.
Однако эта формулировка все же неверна. Если вы мне не верите, посмотрите на следующую передачу параметра типа параметра для объекта:
public static void main(String[] args) {
ParamTest pt = new ParamTest();
String name = "Hollis";
pt.pass(name);
System.out.println("print in main , name is " + name);
}
public void pass(String name) {
name = "hollischuang";
System.out.println("print in pass , name is " + name);
}
Вывод приведенного выше кода
print in pass , name is hollischuang
print in main , name is Hollis
Как это можно объяснить? Также передается объект, но значение исходного параметра не изменилось.Переданный объект снова становится переданным по значению?
Передача по значению в Java
Выше мы привели три примера, но результаты разные, что также является причиной того, что многие начинающие и даже многие продвинутые программисты путаются в типе передачи Java.
На самом деле, я хочу вам сказать, что приведенная выше концепция не является ошибочной, но есть проблема с примером кода. Давайте, я вернусь к основным моментам концепции, а затем приведу несколько действительно подходящих примеров.
Передача по значению относится к передаче фактических аргументов при вызове функции.
复制
В функцию передается копия, так что в функции при правильном参数
Модификации не повлияют на фактические параметры.Передача по ссылке означает передачу адреса фактического параметра при вызове функции.
直接
передается в функцию, затем в функцию参数
Внесенные изменения повлияют на фактические параметры.
Итак, позвольте мне подытожить для вас, в чем смысл разницы между передачей по значению и передачей по ссылке.
В нескольких примерах проходов, которые мы видели выше, мы обращаем внимание только на то, изменилось ли фактическое содержимое параметра. При передаче объекта User мы пытаемся изменить значение его свойства name, а затем проверяем, есть ли изменение. На самом деле экспериментальный метод неверен, и, конечно, полученные выводы также проблематичны.
Почему экспериментальный метод неверен? Здесь мы возьмем визуальный пример. Присмотритесь к передаче по значению и по ссылке, и вы поймете, почему это неправильно.
У тебя есть ключ, когда твой друг хочет пойти к тебе домой, если ты直接
Дайте ему свой ключ, это передача по ссылке. В этом случае, если он что-то сделает с ключом, например, выгравирует на ключе свое имя, то, когда ключ будет возвращен вам, на вашем собственном ключе также будет выгравировано его имя.
У тебя есть ключ, когда твои друзья захотят пойти к тебе домой, ты复刻
Ему дали новый ключ, а его собственный все еще был у него в руке.Это была передача ценности. В этом случае никакие действия, которые он сделает с ключом, не повлияют на ключ в вашей руке.
Но, независимо от описанной выше ситуации, ваш друг взял ключ, который вы ему дали, вошел в ваш дом и разбил ваш телевизор. Так вы говорите, что вас это не коснется? И когда мы меняем значение атрибута name объекта пользователя в методе pass, разве мы не «разбиваем телевизор».
Взяв в качестве примера приведенный выше пример, мы真正的改变参数
, и посмотреть, что происходит?
public static void main(String[] args) {
ParamTest pt = new ParamTest();
User hollis = new User();
hollis.setName("Hollis");
hollis.setGender("Male");
pt.pass(hollis);
System.out.println("print in main , user is " + hollis);
}
public void pass(User user) {
user = new User();
user.setName("hollischuang");
user.setGender("Male");
System.out.println("print in pass , user is " + user);
}
В приведенном выше коде мы изменили пользовательский объект в методе pass, и результат выглядит следующим образом:
print in pass , user is User{name='hollischuang', gender='Male'}
print in main , user is User{name='Hollis', gender='Male'}
Давайте нарисуем картинку и посмотрим, что происходит во всем процессе, а потом я расскажу, почему в Java только передача по значению.
Чтобы немного объяснить эту картину, когда мы создаем объект User в main, мы открываем часть памяти в куче, в которой хранятся такие данные, как имя и пол. то Холлис хранит адрес этой памяти0x123456
(Рисунок 1). При попытке вызвать метод pass и hollis передается в качестве фактического параметра пользователю с формальным параметром, этот адрес будет0x123456
Дайте его пользователю, в это время пользователь также указывает на этот адрес (рисунок 2). Затем, когда параметры изменяются в методе прохода, то естьuser = new User();
, повторно откроет блок0X456789
Память назначается пользователю. Любые последующие модификации пользователя не изменят память0X123456
содержание (рис. 3).
Что за передача выше? По ссылке точно не передается, если передается по ссылке, вuser=new User()
, ссылка на фактический параметр также должна быть изменена, чтобы указывать на0X456789
, но на самом деле это не так.
Благодаря концепции мы также можем знать, что адрес ссылки фактического параметра копируется и передается формальному параметру. так,Вышеупомянутые параметры фактически передаются по значению, а адрес, на который ссылается объект фактического параметра, передается в качестве значения формальному параметру.
Давайте рассмотрим предыдущий пример «разбить телевизор» и посмотрим, что произошло в процессе доставки в этом примере.
Аналогично, в процессе передачи параметра адрес фактического параметра0X1213456
Он копируется в формальный параметр, но в этом методе изменяется не сам формальный параметр, а содержимое, хранящееся в адресе, хранящемся в измененном формальном параметре.
Таким образом, разница между передачей по значению и передачей по ссылке заключается не в том, что передается. Но был ли фактический параметр скопирован в формальный параметр. При оценке того, затрагивается ли содержимое фактического параметра, это зависит от того, что передается.Если вы передаете адрес, то это зависит от того, повлияет ли на него изменение адреса, а не изменение объекта, на который указывает адрес . Как отношения между ключом и домом.
Итак, в этом случае, почему указанный выше объект также передается, а результаты производительности переданного объекта String и объекта User различаются? Используем проходной методname = "hollischuang";
Попробуйте изменить значение имени, случайно адрес ссылки на имя изменится напрямую. Из-за этого кода он создаст новую строку и передаст ссылку на имя, что эквивалентноname = new String("hollischuang");
. Исходная строка «Холлис» по-прежнему удерживается фактическим параметром, поэтому значение фактического параметра не изменяется.
Поэтому Java фактически передается по значению, но для параметров объекта содержимое значения является ссылкой на объект.
Суммировать
Будь то передача по значению или по ссылке, на самом деле это стратегия оценки (Evaluation strategy). В стратегии оценки также есть call by sharing (вызов по обмену). На самом деле передача параметров в Java должна осуществляться путем совместного использования в строгом смысле.
Передача по общему ресурсу означает, что при вызове функции в функцию передается копия адреса фактического параметра (если фактический параметр находится в стеке, значение копируется напрямую). При работе с параметрами внутри функции нужно сначала найти конкретное значение скопированного адреса, а потом действовать. Если значение находится в стеке, потому что это значение скопировано напрямую, работа параметра внутри функции не повлияет на внешнюю переменную. Если исходная копия является адресом исходного значения в куче, то вам нужно найти соответствующую позицию в куче по адресу, а затем действовать. Поскольку передается копия адреса, операции со значением внутри функции видны внешней переменной.
Проще говоря, передача в Java — это передача значения, и это значение на самом деле является ссылкой на объект.
Pass-by-share на самом деле является частным случаем pass-by-value. Таким образом, мы можем сказать, что передача Java является передачей по общему ресурсу или что передача Java является передачей по значению.
использованная литература
Evaluation strategy О передаче по значению и передаче по ссылке передача по значению, передача по ссылке, передача по акции Является ли Java «передачей по ссылке» или «передачей по значению»?