Почему изменяется параметр 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? Кто-нибудь может подробно объяснить?


person Naresh    schedule 08.04.2013    source источник
comment
Назначение null переменной не влияет на объект.   -  person default locale    schedule 08.04.2013
comment
См. Также http://stackoverflow.com/questions/8798403/string-is-immutable-what-exactly-is-the-meaning   -  person Raedwald    schedule 30.04.2014
comment
Читатели из будущего: IMO, сразу переходите к ответу Сикорского!   -  person aderchox    schedule 28.02.2021


Ответы (5)


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

Это происходит из-за того, что Java передает по значению, а Strings неизменны.

Когда ты звонишь

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. но опять же, это просто ссылка на фактическую строку, которая теперь указывает на вновь созданную строку. (из-за неизменности) и исходная строка не изменяется.

person Azodious    schedule 08.04.2013
comment
Это не потому, что строки неизменяемы, а потому, что str = <something> vs list.<something>. - person user253751; 14.06.2014
comment
@immibis, пожалуйста, обоснуйте для лучшего понимания - person Sandeep Rana; 19.05.2016
comment
@SandeepSinghRana Чего ты не понимаешь? - person user253751; 19.05.2016
comment
"это из-за str = ‹something› vs list. ‹something›" что именно это означает - person Sandeep Rana; 19.05.2016
comment
Это НЕ из-за неизменности. попробуйте ниже для пояснения: int t = 9; марка (т); System.out.println (int + t); - person Raúl; 11.11.2016

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

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

person Whymarrh    schedule 08.04.2013

Из книги: SCJP - Учебное пособие для сертифицированного программиста Sun для Java 6 (Катти Сьерра - Берт Бейтс) Глава 3. Задача 7.3 - Передача переменных в методы

Java фактически передает значение для всех переменных, работающих в одной виртуальной машине. Передача по значению означает передачу по значению переменной. А это значит, что переменная будет передаваться по копии!

Итог по передаче по значению: вызываемый метод не может изменить переменную вызывающего, хотя для переменных ссылки на объект вызываемый метод может изменить объект, на который ссылается переменная. В чем разница между изменением переменной и изменением объекта? Для объектных ссылок это означает, что вызываемый метод не может переназначить исходную ссылочную переменную вызывающей стороны и сделать ее ссылкой на другой объект или значение 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, но doStuff () не может изменять фактическое содержимое (битовый шаблон) f. Другими словами, doStuff () может изменить состояние объекта, на который ссылается f, но не может заставить f ссылаться на другой объект!

person Sikorski    schedule 08.04.2013
comment
Я не мог не прокомментировать свою признательность под вашим ответом, несмотря на соглашение SO против этого :) - person aderchox; 28.02.2021

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

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

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

стр = стр + «abc»;

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

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

person Ankur Trapasiya    schedule 08.04.2013

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

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

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

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

person Mordechai    schedule 08.04.2013
comment
это много указателей в ответе Java :) - person Sikorski; 08.04.2013