Java jsp会话:ArrayList<;字符串>;反映最新值,但对于字符串,它不反映最新值

Java jsp会话:ArrayList<;字符串>;反映最新值,但对于字符串,它不反映最新值,java,jsp,session-state,Java,Jsp,Session State,我注意到ArrayList反映了最新的值,但对于String,它不反映 //Initializing List and String ArrayList<String> list = new ArrayList<String>(); list.add("Item1"); String name = "String1"; //Setting Attribute for both session.setAttribute("mylist", list); session.s

我注意到ArrayList反映了最新的值,但对于String,它不反映

//Initializing List and String
ArrayList<String> list = new ArrayList<String>();
list.add("Item1");
String name = "String1";

//Setting Attribute for both
session.setAttribute("mylist", list);
session.setAttribute("myname", name);

//getting attribute for both
out.println("<br> Printing intial valus <br>");
list = (ArrayList<String>)session.getAttribute("mylist");
for (String s:list){
 out.println(s);
}
name = (String) session.getAttribute("myname");
out.println(name);

//updating the values for both
list.add("Item2");
name = "String2";

//Need to add session.setAttribute again for String
//for it to reflect updated value "String 2"
//session.setAttribute("myname", name);

//getting attribute value after the update
list = (ArrayList<String>)session.getAttribute("mylist");
name = (String) session.getAttribute("myname");

//printing the value for both again
out.println("<br><br><br> Prining updated values <br><br>");
for (String s:list){
 out.println(s);
}
out.println(name);

在更新值部分,它不应该打印“String2”,但对于ArrayList,它也打印“Item2”。如果我在更新字符串名称值之后手动添加
session.setAttribute(“myname”,name)
,那么它将打印“String2”。但是ArrayList不需要此session.setAttribute,因为当您将
setAttribute
设置为
会话时,如果该值是字符串,则会话正在内存中保存(复制)字符串值。但是,当值是
数组列表时,它会保存(复制)指向该变量的指针(内存位置而不是值)。

这就是为什么当您从会话中获取并更改值时,指向
ArrayList
的指针不会更改,只有值会更改。因此会话仍在保存正确的指针。但是,当您更改字符串时,该值是change,并且由于会话正在保存旧值,因此不会自动更改。


我希望您能理解我的解释。

我认为,当您将名称添加到会话属性时,名称的值正在被复制。这就是为什么在再次添加之前,您不会看到它更新。然而,list指向一个对象,当您调用add()时,该对象确实会得到更新,因此您可以看到更新,而不做任何进一步的操作

如果您假装自己实现会话属性,则更容易看出这一点。在大多数实现中,属性只是一个
HashMap

HashMap<String,Object> attributes = new HashMap<>();
您应该看到,
name
的值被放入带有以下值的
EntrySet

new EntrySet( "myname", name );
ctor会做一些类似的事情

class EntrySet {
    Object key;
    Object value;
    EntrySet( Object key, Object value ) {
         this.key = key;
         this.value = value;
    }
}
现在我想更容易看出,如果您在原始程序中更改
name
,则
value
的值不会更新

name = "String2"; // does nothing about the contents of EntrySet.value
但是,当您对
ArrayList
执行相同的操作时,很明显,您有两个对同一对象的引用,因此很明显,单个对象只会更新

ArrayList<String> list = new ArrayList<>();
list.add( "Item1" );
ArrayList<String> value = list;  // clearly both list and value point to the same object
value.add( "Item2" );  // the same list just gets updated
ArrayList list=new ArrayList();
列表。添加(“第1项”);
ArrayList值=列表;//显然,列表和值都指向同一个对象
值。添加(“项目2”);//相同的列表刚刚更新
因此,您可以看到列表更新,而无需再次调用
setAttribute()

顺便说一句,拥有这样一个“共享”的对象(列表)是一个巨大的多线程漏洞。如果可能,您应该改为在
请求
对象上设置属性。否则,您必须锁定会话或提供其他形式的互斥锁,以确保所有内容的安全和同步。

ArrayList list=new ArrayList();
ArrayList<String> list = new ArrayList<String>();
list.add("Item1");
String name = "String1";
列表。添加(“第1项”); String name=“String1”;
上面的几行创建了一个名为list的变量,该变量指向内存中具有列表元素“Item1”的位置(将该位置命名为locatinoLOC123) 您还创建了一个名为name的变量,该变量指向内存中的一个位置(将locatinoLOC456命名),该位置的字符串值为“String1”

名称列表变量传递给会话.setAttribute方法时,只传递了列表名称变量的参考值。 现在会话对象在内存中引用LOC123&LOC456,这允许它检索实际值

当您执行list.add(“Item2”)时命令您向同一内存位置添加了一个以上的元素LOC123。鉴于会话已经指向LOC123,您设法看到了反映的更改值

Java字符串是不可变的,一旦创建它们,就不能更改它们。当您执行name=“String2”您在内存中创建了一个新位置LOC789,其字符串值为“String2”,名称变量现在已更改为指向LOC789

假定会话对象仍然指向LOC456,并且它对LOC789一无所知,则更新的“String2”值未反映在会话中


要解决此问题,您可以使用可编辑的StringBuffer(或StringBuilder)。

当您将
String2
分配给
name
时,您可以这样想
上一个
String
对象
String1
失去了它的引用,但是会话仍然具有属性
myname
的值,它没有返回null

因此,当您编写类似于
session.setAttribute(“myname”,name)的代码时名称和会话属性都位于两个不同的内存位置,这就是为什么名称中的更改不会反映在会话中的原因


当您将object作为参数传递给Java的method时,可以在这个链接中检查
String
类的特例
ArrayList
是可变的,因此:

List<String> list = new ArrayList<String>();
// copy of object reference passed to session
session.setAttribute("list", list);
// adding new value to same object, so all references to it will see the chages
list.add("Something");

我认为现在的情况是,当您将
name
添加到会话属性时,它的值会被复制。这就是为什么在再次添加之前,您不会看到它更新。然而,
list
指向一个对象,当您调用
add()
时,该对象确实会得到更新,因此您可以看到更新,而不做任何进一步的操作。这是不正确的,对String和ArrayList都传递了对方法的引用。主要区别在于list.add(…)更新初始对象的值,而String name=“new”将新对象分配给变量名,而不更新初始对象。
ArrayList<String> list = new ArrayList<String>();
list.add("Item1");
String name = "String1";
List<String> list = new ArrayList<String>();
// copy of object reference passed to session
session.setAttribute("list", list);
// adding new value to same object, so all references to it will see the chages
list.add("Something");
// object "value" created, reference passed to 'name' variable
String name = "value";
// copy of reference to object value passed to session
session.setAttribute("string", name);
// NEW object "otherString" created and its reference assigned to variable 'name'
// session still has it's copy of reference to "value" object
name = "otherString";