Правда о передаче параметров Java: это передача по значению или по ссылке? 🎭

Java — это популярный объектно-ориентированный язык программирования, который существует уже более двух десятилетий. Одной из фундаментальных концепций Java является передача параметров, которая относится к механизму, с помощью которого аргументы передаются методам. Однако вопрос о том, передает ли Java параметры по значению или по ссылке, является предметом многочисленных споров и путаницы среди разработчиков. Хотя обычно говорят, что Java передается по значению, реальность такова, что она ведет себя по-разному в зависимости от типа передаваемого параметра. В этой статье мы рассмотрим тонкости передачи параметров Java, обсудим различия между передачей по значению и передачей по ссылке и то, как они проявляются в Java. Мы также рассмотрим поведение объектов, примитивов и классов-оболочек, а также некоторые распространенные заблуждения о передаче параметров Java. К концу этой статьи вы лучше поймете эту важную концепцию программирования на Java.

Во-первых, но что означает передача по значению и передача по ссылке?

В языках программирования существует два распространенных способа передачи параметров методам или функциям: передача по значению и передача по ссылке.

При передаче по значению копия значения параметра передается методу или функции. Это означает, что любые изменения параметра внутри метода или функции не влияют на исходное значение параметра вне метода или функции. Другими словами, метод или функция работает с локальной копией значения параметра, и любые изменения, внесенные в эту копию, не влияют на исходное значение параметра.

При передаче по ссылке ссылка на параметр передается методу или функции. Это означает, что любые изменения параметра в методе или функции влияют на исходное значение параметра вне метода или функции. Другими словами, метод или функция работает непосредственно с самим параметром, и любые изменения, внесенные в параметр, отражаются в исходном значении параметра.

В Java всегда было много споров по этому поводу, и хотя теория гласит, что Java всегда передает параметры по значению, мы собираемся объяснить, как это ведет себя в зависимости от типа данных, которые передаются в качестве аргумента.
В основном мы можем найти 2 типа данных: примитивные и не примитивные.

В примитивных данных мы можем найти следующие типы: boolean, byte, int, short, double, long, float и char.

Что касается непримитивных данных, мы можем говорить о: String, Array, List (любая предопределенная структура данных) и обертках классов примитивных типов (Boolean, Integer, Double и т.д.)

Примитивные типы данных

Примитивные данные передаются методам или функциям по значению и ведут себя так, как мы обсуждали ранее (если мы изменим его значение в методе, исходная переменная не изменится).

public class Main {
  public static void main(String[] args) {
    int x = 10;
    System.out.println("Before calling the method: " + x);
    changeValue(x);
    System.out.println("After calling the method: " + x);
  }

  public static void changeValue(int value) {
    value = 20;
    System.out.println("Within the method: " + value);
  }
}

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

Перед вызовом метода: 10
Внутри метода: 20
После вызова метода: 10

Непримитивные типы данных

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

public class Main {
  public static void main(String[] args) {
    Person p = new Person("John", 20);
    System.out.println("Before calling the method: " + p);
    changeName(p);
    System.out.println("After calling the method: " + p);
  }

  public static void changeName(Person person) {
    person.setName("Peter");
    System.out.println("Within the method: " + person);
  }
}

class Person {
  private String name;
  private int age;

  public Person(String name, int age) {
    this.name = name;
    this.age = age;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }

  @Override
  public String toString() {
    return "Person [name=" + name + ", age=" + age + "]";
  }
}

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

Перед вызовом метода: Person [name=John, age=20]
Внутри метода: Person [name=Peter, age=20]
После вызова метода: Person [ имя=Питер, возраст=20]

Внимательно глядя на пример, можно подумать, что я ошибаюсь, если я сказал ранее, что Java всегда передает по значению аргументы, и они не изменяются, почему этот тип данных изменяется внутри функции? Мы можем объяснить это следующим образом:

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

Например, если вы передаете объект типа List в метод и добавляете элемент в список внутри метода, вы изменяете фактический объект, на который указывают обе ссылки. Хотя ссылка, переданная методу, является копией, обе ссылки указывают на один и тот же реальный объект в памяти, поэтому любые изменения, сделанные в методе, повлияют на реальный объект.

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

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

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

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

Я надеюсь, что этот пост поможет вам прояснить все эти понятия! 🧠