от 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 аргументы всегда передаются по значению, копия будет либо ссылкой, либо переменной в зависимости от исходного типа переменной. С этого момента вы можете использовать следующие советы, чтобы понять, как изменение аргументов внутри метода влияет на исходную переменную:
- Изменение значения примитивного аргумента никогда не повлияет на исходную переменную.
- Изменение ссылки аргумента объекта внутри метода никогда не повлияет на исходную ссылку, однако создает совершенно новый объект в пространстве кучи.
- Изменение атрибутов аргумента объекта внутри метода отражается за его пределами.
- Изменение коллекций и карт внутри метода отражается за его пределами.
Теги: #ява
Первоначально опубликовано на www.programmergate.com 15 июля 2017 г.