【问题标题】:jsp session: ArrayList<String> reflects the latest value but for String, it does notjsp session: ArrayList<String> 反映了最新的值,但对于 String,它没有
【发布时间】:2018-04-09 17:21:12
【问题描述】:

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

【问题讨论】:

  • 我认为当您将 name 添加到会话属性时,它的值会被复制。这就是为什么在您再次添加之前看不到它更新的原因。而list 指向一个在您调用add() 时确实会更新的对象,因此您无需进一步操作即可看到更新。

标签: java jsp session-state


【解决方案1】:

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

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

希望你能理解我的解释。

【讨论】:

  • 不正确,引用传递给 String 和 ArrayList 的方法。 list.add(...) 更新初始对象的值的主要区别,但是 String name = "new" 将新对象分配给变量名称而不更新初始对象。
【解决方案2】:

我认为当您将 name 的值添加到会话属性时,它的值被复制了。这就是为什么在您再次添加之前看不到它更新的原因。而 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() 即可看到列表更新的原因。

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

【讨论】:

    【解决方案3】:
    ArrayList<String> list = new ArrayList<String>();
    list.add("Item1");
    String name = "String1";
    

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

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

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

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

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

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

    【讨论】:

      【解决方案4】:

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

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

      你可以在这个链接What is the difference between “text” and new String(“text”)?查看String类的特殊情况

      【讨论】:

        【解决方案5】:

        当您将对象作为参数传递给 Java 方法时 - 其 引用 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";
        

        【讨论】:

          猜你喜欢
          • 2012-07-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-03-10
          • 2014-09-27
          • 2013-10-05
          • 2014-01-02
          • 2015-12-07
          相关资源
          最近更新 更多