jsp session: ArrayList<String> 反映最新值但对于 String,它不

jsp session: ArrayList<String> reflects the latest value but for String, it does not

我观察到 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);

下面的输出是

Printing intial valus 
 Item1 String1 

 Prining updated values 
 Item1 Item2 String1 Session 2

在更新值部分,它不应该打印 "String2",但对于 ArrayList,它也打印 "Item2"。如果我在更新字符串名称值之后手动添加 session.setAttribute("myname", name),那么它会打印 "String2"。但是 ArrayList

不需要这个 session.setAttribute

这是因为当你setAttributesession时,如果值是一个字符串,那么你的session是在内存中保存(复制)一个字符串值。但是,当值是 ArrayList 时,它会保存(复制)指向该变量的指针(内存位置而不是值)。

这就是为什么当您从会话中获取和更改值时,指向 ArrayList 的指针不会更改,只有值会更改。所以session还是在保存右指针。但是,当你改变一个字符串时,值是改变的,因为会话保存旧值,它不会自动改变。

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

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

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

HashMap<String,Object> attributes = new HashMap<>();

因此,如果您向其中添加一个字符串:

String name = "String1";
attributes.put( "myname", name );

您应该看到 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

这就是为什么您无需再次调用 setAttribute() 即可看到列表更新的原因。

顺便说一句,拥有一个像这样的对象 "shared"(list)是一个巨大的多线程漏洞。如果可能,您应该改为在 request 对象上设置属性。如果没有,您将不得不锁定会话或提供其他形式的互斥锁以确保一切安全和同步。

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

上面的行创建了一个名为 list 的变量,它指向内存中的一个位置(让我们命名为 locatino LOC123),它有一个列表元素“Item1” 您还创建了一个名为 name 的变量,它指向内存中的一个位置(让我们命名为 locatino LOC456),它的字符串值为“String1"

当您将 name & list 变量传递给 session.setAttribute 时方法你只传递了 list & name 变量的参考值。 现在会话对象在内存中引用 LOC123 & LOC456,这允许它检索实际值。

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

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

鉴于会话对象仍然指向 LOC456 并且它对 LOC789 一无所知,更新后的“String2" 值没有反映在会话中。

要克服这个问题,您可以使用可编辑的 StringBuffer(或 StringBuilder)。

你可以这样想,当你将 String2 分配给 name 时,就像 name = "String2"; 之前的 String 对象 String1 失去了它的引用,但是session 仍然具有属性 myname 的值,它没有返回 null。

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

你可以在这个linkWhat is the difference between “text” and new String(“text”)?

中查看Stringclass的特例

当您将对象作为参数传递给 Java 的方法时 - 它的 reference copied and passed to it 的值。 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");

String 类型是 不可变的:

// 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";