от husseinterek · 15 июля 2017 г.

Прежде чем описывать, как передаются аргументы в java, стоит определить, как java-переменные размещаются внутри памяти. В основном мы говорим о двух типах переменных: примитивы и объекты.

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

1. По значению VS По ссылке

Что подразумевается под «По значению» и «По ссылке»:

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

2. Как передаются аргументы в java?

В Java аргументы всегда передаются как значение независимо от исходного типа переменной. При каждом вызове метода происходит следующее:

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

3. Исправление некоторых проблем !!

В следующем примере мы пытаемся проверить, что "java всегда передается по значению", передавая несколько типов аргументов (примитивы, оболочки, коллекции, бизнес-объекты) и проверяя, они изменяются после вызова метода.

  • Передача примитивных аргументов:
public static void main(String[] args) {
     int x = 1;
     int y = 2;
     System.out.print(“Values of x & y before primitive modification: “);
     System.out.println(“ x = “ + x + “ ; y = “ + y );
     modifyPrimitiveTypes(x,y);
     System.out.print(“Values of x & y after primitive modification: “);
     System.out.println(“ x = “ + x + “ ; y = “ + y );
}
private static void modifyPrimitiveTypes(int x, int y)
{
    x = 5;
    y = 10;
}
  • Вывод:
Values of x & y before primitive modification: x = 1 ; y = 2
Values of x & y after primitive modification: x = 1 ; y = 2
  • Описание вывода:

Две переменные x и y имеют примитивные типы и хранятся в памяти стека. При вызове modifyPrimitiveTypes()в памяти стека создаются 2 копии (скажем, w и z), которые затем передаются методу. Следовательно, исходные переменные не отправляются в метод, и любые изменения внутри потока метода влияют только на копии.

  • Передача аргументов оболочки:
public static void main(String[] args) {
     Integer obj1 = new Integer(1);
     Integer obj2 = new Integer(2);
     System.out.print(“Values of obj1 & obj2 before wrapper modification: “);
     System.out.println(“obj1 = “ + obj1.intValue() + “ ; obj2 = “ + obj2.intValue());
     modifyWrappers(obj1, obj2);
     System.out.print(“Values of obj1 & obj2 after wrapper modification: “);
     System.out.println(“obj1 = “ + obj1.intValue() + “ ; obj2 = “ + obj2.intValue());
}
private static void modifyWrappers(Integer x, Integer y)
{
    x = new Integer(5);
    y = new Integer(10);
}
  • Вывод:
Values of obj1 & obj2 before wrapper modification: obj1 = 1;obj2 = 2
Values of obj1 & obj2 after wrapper modification: obj1 = 1;obj2 = 2
  • Описание вывода:

Обертки хранятся внутри памяти кучи с соответствующей ссылкой внутри памяти стека.

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

PS: если вы измените значение объектов-оболочек внутри метода следующим образом: x += 2,изменение не отразится за пределами метода, поскольку объекты-оболочки неизменяемы, что означает что они создают новый экземпляр каждый раз, когда их состояние изменяется. Для получения дополнительной информации о неизменяемых классах см. Как создать неизменяемый класс в java. Строковые объекты работают аналогично оберткам, поэтому приведенные выше правила применимы и к строкам.

  • Аргумент передачи коллекции:
public static void main(String[] args) {
    List<Integer> lstNums = new ArrayList<Integer>();
    lstNums.add(1);
    System.out.println(“Size of list before List modification = “ + lstNums.size());
    modifyList(lstNums);
    System.out.println(“Size of list after List modification = “ + lstNums.size());
}
private static void modifyList(List<Integer> lstParam)
{
     lstParam.add(2);
}
  • Вывод:
Size of list before List modification = 1
Size of list after List modification = 2
  • Описание вывода:

При определении Arraylist или любой коллекции в java внутри стека создается ссылка, которая указывает на несколько объектов внутри динамической памяти при вызове modifyList(), создается копия ссылки и передается методу, так что на фактические данные объекта ссылаются 2 ссылки, и любое изменение, сделанное одной ссылкой, отражается на другой.

Внутри метода мы вызвали lstParam.add(2), который на самом деле пытается создать новый объект Integer в памяти кучи и связать его с существующим списком объектов. Следовательно, ссылка на исходный список может видеть изменение, поскольку обе ссылки указывают на один и тот же объект в памяти.

  • Передача бизнес-объекта в качестве аргумента:
public static void main(String[] args) {
    Student student = new Student();
    System.out.println(“Value of name before Student modification =    “ + student.getName()); 
    modifyStudent(student);
    System.out.println(“Value of name after Student modification = “ + student.getName());
}
private static void modifyStudent(Student student)
{
      student.setName(“Alex”);
}
  • Вывод:
Value of name before Student modification = null
Value of name after Student modification = Alex
  • Описание вывода:

Объект student создается внутри пространства кучи, а ссылка на него определяется внутри стека, при вызове modifyStudent() внутри стека создается копия ссылки и передан методу. Любые изменения атрибутов объекта внутри метода отражаются в исходной ссылке.

4. Вывод

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

  1. Изменение значения примитивного аргумента никогда не повлияет на исходную переменную.
  2. Изменение ссылки аргумента объекта внутри метода никогда не повлияет на исходную ссылку, однако создает совершенно новый объект в пространстве кучи.
  3. Изменение атрибутов аргумента объекта внутри метода отражается за его пределами.
  4. Изменение коллекций и карт внутри метода отражается за его пределами.

Теги: #ява

Первоначально опубликовано на www.programmergate.com 15 июля 2017 г.