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
这是因为当你setAttribute
到session
时,如果值是一个字符串,那么你的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”)?
中查看String
class的特例
当您将对象作为参数传递给 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";
我观察到 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
这是因为当你setAttribute
到session
时,如果值是一个字符串,那么你的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”)?
中查看String
class的特例
当您将对象作为参数传递给 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";