Подтвердить что ты не робот

Почему параметр ArrayList изменен, но не параметр String?

public class StackOverFlow {
    public static void main(String[] args) {
        ArrayList<String> al = new ArrayList<String>();
        al.add("A");
        al.add("B");
        markAsNull(al);
        System.out.println("ArrayList elements are "+al);

        String str = "Hello";
        markStringAsNull(str);
        System.out.println("str "+ str);
    }
    private static void markAsNull(ArrayList<String> str){
        str.add("C");
        str= null;
    }
    private static void markStringAsNull(String str){
        str = str + "Append me";
        str = null;
    }
}

Вывод:

ArrayList elements are [A, B, C]
str Hello

В случае ArrayList добавляются добавленные элементы. В случае String вызов метода не влияет на переданную строку. Что именно делает JVM? Может кто-нибудь объяснить подробно?

4b9b3361

Ответ 1

В случае строковых объектов Arraylist добавляются новые элементы. В случае String вызов метода не влияет на переданную строку.

Это происходит потому, что Java является Pass-by-Value и String являются неизменяемыми

Когда вы вызываете

markAsNull(ArrayList<String> str)

Новая ссылка по имени str создается для той же ArrayList, на которую указывает al. Когда вы add элемент на str, он добавляется к одному и тому же объекту. Позже вы ставите str в null, но объект добавляет новые значения и нажимает a1.

Когда вы вызываете

markStringAsNull(String str)
{
    str = str + "Append me";
    // ...
}

Строка str = str + "Append me"; создает новый объект String, добавляя данную строку и присваивая ее str. но опять же это просто ссылка на фактическую строку, которая теперь указывает на вновь созданную строку. (из-за невосприимчивости), а исходная строка не изменяется.

Ответ 2

Методы markXAsNull устанавливают локальные ссылки как null. Это не влияет на фактическое значение, хранящееся в этом месте. Метод main все еще имеет свои собственные ссылки на значения и может вызывать println с помощью этих.

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

Ответ 3

Java следует за концепцией значения passby (в Java нет прохода по ссылке). Поэтому, когда вы передаете строку функции, она отправляет "копию ссылки в эту строку функции. Поэтому, даже если вы установите переменную в значение null в функции, когда она вернется к вызывающей стороне, она ссылается только на ее исходное значение. Поэтому исходная строка не имеет эффекта.

В случае Arraylist копия ссылки относится к оригинальному Arraylist (который также является одним и тем же в случае строки). Но когда вы добавляете что-то в ArrayList, это относится к исходному объекту и поэтому вы можете увидеть эффект. (попробуйте в методе arraylist = new ArrayList(), ваш первоначальный arraylist останется таким, какой он есть).

В случае строки, когда вы делаете

str = str + "abc";

Java создает новый объект String, который будет ссылаться на строку "xyzabc" (например, str = "xyz" ), а "xyz" будет иметь право на сбор мусора. Но поскольку "xyz" все еще имеет переменную, которая ссылается на нее, она (исходная строка) не будет собираться мусором. Но как только вызов функции переполняется, "xyzabc" отправляется на сборку мусора.

Резюме обсуждения состоит в том, что если ссылка ссылается на тот же объект, вы можете вносить изменения в функцию, но когда вы пытаетесь изменить ссылку (str = str + "abc" ), вы ссылаетесь на новый объект в методе, чтобы ваш исходный объект оставался таким, каким он есть.

Ответ 4

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

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

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

Еще одна точка, поскольку String неизменен, вы фактически получите новый объект, отличный от оригинала (из того, что вы должны сказать a = a + "a"), поэтому он не будет изменять исходная строка.

Ответ 5

Из книги: SCJP - Sun Certified Programmer для Java 6 Study Guide (Katty Sierra - Bert Bates) Глава 3 Цель 7.3 - Передача переменных в методы

Java фактически является передачей по значению для всех переменных, работающих в одном VM. Пошаговое значение означает pass-by-variable-value. И это означает, что по-копии- переменная!

Нижняя строка для пропущенного значения: вызываемый метод не может изменить вызывающий переменная, хотя для ссылочных переменных объекта вызываемый метод может изменить объект, на который ссылается переменная. Какая разница между изменением переменной и изменение объекта? Для ссылок на объекты это означает, что вызываемый метод не может переназначить исходную ссылочную переменную вызывающего и сделать ее ссылкой на другой объект, или null. Например, в следующем фрагменте кода

void bar() {
Foo f = new Foo();
doStuff(f);
}
void doStuff(Foo g) {
g.setName("Boo");
g = new Foo();
}

переназначение g не переназначает f! В конце метода bar() два объекта Foo были созданы, причем одна из них ссылается на локальную переменную f и одну, на которую ссылается локальная (аргументная) переменная g. Поскольку метод doStuff() имеет копию ссылочной переменной, у него есть способ добраться до исходного объекта Foo, например, для вызова метод setName(). Но метод doStuff() не имеет возможности добраться до контрольная переменная f. Поэтому doStuff() может изменять значения внутри объекта f to, но doStuff() не может изменить фактическое содержимое (битовая диаграмма) f. В других слова, doStuff() может изменять состояние объекта, к которому относится f, но он не может сделайте f ссылкой на другой объект!