【问题标题】:Understanding Reference to an Object from within a Java Object Array了解从 Java 对象数组中对对象的引用
【发布时间】:2015-02-05 06:59:43
【问题描述】:

我编写了一些代码来了解如果我将基元、可变或不可变对象的混合存储到 Array 对象中会发生什么。我可以在存储后修改它们,看看它是否正确取消引用这些东西并返回它们修改后的值。我没有得到我期望我的代码做的事情?我想我知道为什么并且想澄清这种理解是否正确。这是代码。

public class DriverApp3 {

private static String CYEAR = "2014" ;
private static StringBuffer CYEARFLG = new StringBuffer("1914") ;

public double money = 2.13 ;

public static void main(String args[])
{
    Integer j = 12 ;
    DriverApp3 d = new DriverApp3(); 
    StringBuffer sb = new StringBuffer("Unicorn");
    MutableInteger mi = new MutableInteger(67);
    int i = 76 ;

    Object[] parseXML = new Object[]{j,DriverApp3.CYEAR,d, d.money,DriverApp3.CYEAR, sb, mi, DriverApp3.CYEARFLG,i};

    //                               0           1      2      3        4             5   6           7          8
    System.out.println("======chng of all original values ==========");
    j = 13 ; 
    d.money = 3.14 ; 
    parseXML[4]="2015";
    DriverApp3.CYEAR = "2013"; 
    mi.set(9);
    sb.replace(3,5,"KO");
    DriverApp3.CYEARFLG.replace(0,4,"1939");
    i = 7 ;

    Object[] chngdO = new Object[]{j,DriverApp3.CYEAR,d, d.money,DriverApp3.CYEAR, sb, mi, DriverApp3.CYEARFLG,i};

    int cnt = 0 ;
    for (Object m : parseXML)
    {
        Integer s_objid = m.hashCode();
        String clsType = "Type="+m.getClass().getTypeName();
        String clsName = "SimplName="+m.getClass().getSimpleName();
        String canName = "CanonName="+m.getClass().getCanonicalName();

        Object n = chngdO[cnt];
        Integer ns_objid = n.hashCode();
        String nclsType = "Type="+n.getClass().getTypeName();
        String nclsName = "SimplName="+n.getClass().getSimpleName();
        String ncanName = "CanonName="+n.getClass().getCanonicalName();


        System.out.println(cnt + ": Hashcode=" + s_objid + ":" + clsType + ":" + m + "\n " + ": Hashcode=" + ns_objid + ":" + nclsType /*+ ":"+ clsName+ ":"+  canName*/+ ":" + n + "\n" );
        cnt++ ;
    }

}

@Override
public String toString()
{

    return "Hashcode="+this.hashCode() + "," + DriverApp3.CYEAR  ;
} 

}

可变类也在这里.....

/** * * @author 代码 sn-p 来自 stackoverflow.com * 不是线程安全的 */ 公共类 MutableInteger {

private int value;
public MutableInteger(int value) {
    this.value = value;
}
public void set(int value) {
    this.value = value;
}
public int intValue() {
    return value;
}

public String toString()
{
    return "id="+this.hashCode()+" val=" + this.value ; 
}

}

我的应用程序的输出是.....

======chng of all original values ==========

0: 哈希码=12:Type=java.lang.Integer:12 : Hashcode=13:Type=java.lang.Integer:13

1: 哈希码=1537249:Type=java.lang.String:2014 : Hashcode=1537248:Type=java.lang.String:2013

2: Hashcode=366712642:Type=xander.DirRefOrCopy.DriverApp3:Hashcode=366712642,2013 : Hashcode=366712642:Type=xander.DirRefOrCopy.DriverApp3:Hashcode=366712642,2013

3: 哈希码=815979831:Type=java.lang.Double:2.13 : Hashcode=300063655:Type=java.lang.Double:3.14

4: 哈希码=1537250:Type=java.lang.String:2015 : Hashcode=1537248:Type=java.lang.String:2013

5: 哈希码=1829164700:Type=java.lang.StringBuffer:UniKOrn : Hashcode=1829164700:Type=java.lang.StringBuffer:UniKOrn

6: 哈希码=2018699554:Type=xander.DirRefOrCopy.MutableInteger:id=2018699554 val=9 : Hashcode=2018699554:Type=xander.DirRefOrCopy.MutableInteger:id=2018699554 val=9

7: 哈希码=1311053135:Type=java.lang.StringBuffer:1939 : Hashcode=1311053135:Type=java.lang.StringBuffer:1939

8: 哈希码=76:Type=java.lang.Integer:76 : Hashcode=7:Type=java.lang.Integer:7

【问题讨论】:

    标签: java arrays pointers memory


    【解决方案1】:

    索引 0 对于整数 j,它是不可变的,所以一旦我在数组外修改它,第 0 个仍然指向值为 12 的那个,因为它仍然存在于 Java 保存它的那个整数的不可变单个副本的任何地方。这显然是一个保留每个类或接口的所有运行时常量的区域。在这种情况下,它是字符串文字区域。修改后的 j 指向一个新的不可变的值 13 的单一副本。请参阅哈希码也不同。

    索引 1 和上面一样的问题。仅仅因为它是 DriverApp3 的成员,您会期望自己修改它,但它不会因为数组存储了内存地址 String 的旧值,java 存储了该不可变的单个副本。因为,那个地址仍然是“活动的”,它一直指向数组中的那个内存地址。非常非常不直观 - 不确定是否有其他模式或语言可以更好地处理这个问题。这可能是错误的来源。想要确保您反映您的更改 - 在其中使用 MUTABLE 对象而不是 IMMUTABLE。或者,您有额外的思考开销,必须记住 Java 可能会根据它是 String、Integer、Double 等,将您的原语(如果您存储原语)自动装箱到 Object 文字中。

    索引 2 一切都按预期工作 - 它指向数组。因此,它在更改时继续指向相同的可变对象地址。

    索引 3 再次将双精度自动装箱为不可变的双精度。因此,它与 String 和 Integer 是相同的问题,因为它是一个对象数组。当然,从 Java 1 开始就是这样 - 顺便说一句 - 我在这里使用 Java 8 编译器。

    索引 4 parseXML[4]="2015"; 那么这里我们直接用不可变字符串字面量 2015 的地址覆盖地址

    索引 5 和 6 StringBuffer 和 MutableInteger 正确反映了修改,因为它们仍然指向相同的地址。 Java 不会创建新副本,因为它们被定义的本质是可变的。

    索引 7 反映更改,因为它是可变的 StringBuffer 而不是共享的不可变文字!

    索引 8 遭受同样的问题,即原语被自动装箱为不可变的共享单副本文字。

    因此,如果您希望存储数组的内容反映您在数组之外修改的更改,请使用可变对象。并且,当心自动自动装箱到不可变对象字面量的陷阱。

    【讨论】:

      猜你喜欢
      • 2011-08-25
      • 2011-06-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-05
      • 2019-10-03
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多