更新:

  在一次搜索“变量声明在循环体内还是循环体外”问题时,碰见了一个这样的代码,与本文类似,代码如下:

Document [] old ......//这是数据源
EntityDocument[] newArray = new EntityDocument[old.length];//自定义的类,为了把Document里数据保留下来避免Document被关联对象关闭而导致无法取出数据。
EntityDocument d = new EntityDocument();
for(int i=0;i<old.length;i++){
d.content = old[i].getContent();
d.key = old[i].getKey();
d......................
newArray[i] = d;//如此对象重用.....
}

  上面的代码最终结果会导致newArray数组中的每个元素都等于最后一个元素,原因就是每次向newArray中存储对象时,没有新建一个对象进行储存,从而导致错误。

  上面代码只需要将第3行的 EntityDocument d = new EntityDocument(); 放入循环体内即可实现正常功能。

  这种错误隐藏得比较深,所以要好好记住。

原文

  在刷《剑指OFFER》的时候,自己犯了一个错误,发现:在链表中存储一个对象时,如果该对象是不断变化的,则应该创建一个新的对象复制该对象的内容(而不是指向同一个对象),将这个新的对象存储到链表中。如果直接存储该对象的话,链表中的对象也会不断变化。基本数据类型和String则没有这种问题。

其实这归根结底是一个传值和传引用的问题:

  1.如果存储到链表中的是对象,则存储的是引用(地址),所以该地址上的内容变化时,会引起对象的变化。

  2.如果存到链表中的是基本数据类型或者String,存储的就是该数值,不会再发生变化了。(其实String是对象,存储的是引用,后面讨论)。

举个例子:

import java.util.ArrayList;

public class Test {
public static class Person {
int age=1;
}
public static void main(String[] args) {
//=======ArrayList存储String或者基础数据类型=========
ArrayList<String> list = new ArrayList<>();
String aString="abc";
list.add(aString);
System.out.println("before:"+list.toString());
aString="123";
System.out.println("after:"+list.toString()); //========ArrayList存储对象=======
ArrayList<Person> pList = new ArrayList<>();
Person a = new Person();
Person b = new Person();
b = a;
Person c = new Person();
c.age=a.age;
pList.add(a);
pList.add(b);
pList.add(c); System.out.print("before:");
for (Person person : pList) {
System.out.print(person.age+" "); //a,b,c的age此时都是1
}
System.out.println(); a.age=2;
System.out.print("after:");
for (Person person : pList) {
System.out.print(person.age+" "); //输出:2,2,1
}
//关键原因:b是和a指向同一个对象,c不是同一个对象
}
}

  

before:[abc]
after:[abc]
before:
after:

  上面的代码可以知道,存储Person这个对象时(存储的是地址),b和a其实是同一个地址,所以a指向的对象改变,会引起链表中的前两个结点(地址相同)改变,而如果要使存进链表的person存储的是a存储时的状态,只能新建一个对象c,令c的内容等于a,才在后面不会发生变化(因为该地址指向的内容没有再发生改变了)。

  关于String的讨论:其实String也是对象,存储的其实也是引用(地址),但为什么上面代码中before和after输出的内容都是“abc”呢?其实在aString="123";时,相当于aString=new String("123"),即aString指向了另一个对象,aString存储的地址变成了“123”的地址,但链表中存储的还是“abc”的地址,所以链表中的内容不变。

  更详细的传递讨论:值传递和引用传递讨论

【Java】链表中存储对象的问题的更多相关文章

  1. JAVA链表中迭代器的实现

    注:本文代码出自<java数据结构和算法>一书. PS:本文中类的名字定义存在问题,Link9应改为Link.LinkList9应该为LinkList.由于在同包下存在该名称,所以在后面接 ...

  2. Java 开发中的对象拷贝

    前言 在 Java 开发中,很多时候需要将两个属性基本相同的对象进行属性复制,比如 DO 转 VO等等. 本文主要介绍自己实现的简易拷贝工具类与 Spring 提供的属性拷贝的对比. Spring 提 ...

  3. COM结构化存储中存储对象或者流对象的命名规则

      COM结构化存储中存储对象或者流对象的命名规则

  4. java内存中的对象

    前记:几天前,在浏览网页时偶然的发现一道以前就看过很多遍的面试题,题目是:“请说出‘equals’和‘==’的区别”,当时我觉得我还是挺懂的,在心里答了一点(比如我们都知道的:‘==’比较两个引用是否 ...

  5. 面试题:JVM在Java堆中对对象的创建、内存结构、访问方式

    一.对象创建过程 1.检查类是否已被加载 JVM遇到new指令时,首先会去检查这个指令参数能否在常量池中定位到这个类的符号引用,检查这个符号引用代表的类是否已被加载.解析.初始化,若没有,则进行类加载 ...

  6. Java操作Redis存储对象类型数据

    背景描述      关于JAVA去操作Redis时,如何存储一个对象的数据,大家是非常关心的问题,虽然官方提供了存储String,List,Set等等类型,但并不满足我们现在实际应用.存储一个对象是是 ...

  7. 在localStorage中存储对象数组并读取

    频繁ajax请求导致页面响应变慢. 于是考虑将数据存储在window.storage中,这样只请求一次ajax,而不需要频繁请求. 鉴于localstorage中只能存储字符串,所以我们要借助于JSO ...

  8. Java连接Redis,存储对象获取对象()byte和json),连接池

    Java连接Redis Jedis连接Redis,Lettuce连接Redis Jedis连接Redis 1. 创建maven项目 2. 引入依赖 <dependencies> <d ...

  9. java list中的对象去重原理

    /******************************************************************************* * * Copyright (c) W ...

随机推荐

  1. loj6253/luogu4062-Yazid的新生舞会

    先考虑部分分(只有01/只有0~7)做法:枚举每个数,把和他相同的设为1,不同的设为-1,然后这个数作为众数贡献的个数就是区间和>0的个数 推着做,树状数组记前缀和<=x的区间的数量就可以 ...

  2. 使用ImageMagick 在图片上绘制粗斜体的中文也许是一个错误。

    测试发现: ImageMagick使用中文字体,在图片上绘制带粗或斜体的中文,看不到效果. 如果使用英文字体,绘制粗或斜体的英文,99%都有效果. 今天无意看到一篇文章提到: convert -lis ...

  3. VMware Workstation 12序列号:

    VMware Workstation 12序列号: 5A02H-AU243-TZJ49-GTC7K-3C61N

  4. Hadoop生态圈-Kafka的旧API实现生产者-消费者

    Hadoop生态圈-Kafka的旧API实现生产者-消费者 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.旧API实现生产者-消费者 1>.开启kafka集群 [yinz ...

  5. DataGridView刷新数据

    在DataGridView上操作数据之后,无论是增删改都是对数据库进行了操作,而DataGridView这个控件在操作之后是不会变化的,需要重新的去数据库里读取一下数据才行,可以理解为之刷新 Data ...

  6. bzoj千题计划286:bzoj1226: [SDOI2009]学校食堂Dining

    http://www.lydsy.com/JudgeOnline/problem.php?id=1226 关键点:一个人只能忍受 ‘紧跟’ 在他 后面的b个人比他先打到饭 dp[i][j][k] 前i ...

  7. 【学习笔记】Spring AOP注解使用总结

    Spring AOP基本概念 是一种动态编译期增强性AOP的实现 与IOC进行整合,不是全面的切面框架 与动态代理相辅相成 有两种实现:基于jdk动态代理.cglib Spring AOP与Aspec ...

  8. 关于Asp.Net中的编程实现下载

    经常在论坛看见有人求Asp.Net中编程实现下载的代码,有些还希望能断点续传什么的.其实问题的关键在于权限.B/S和C/S不仅仅是外观上的区别而已. 下载,顾名思义是客户端要下,所以载.你硬塞給人家那 ...

  9. 第13月第10天 swift3.0

    1. Type 'Any' has no subscript members 这一条简直莫名其妙.大体意思就是,你这个类型"Any"不是个数组或者字典,不能按照下标取东西. 我之前 ...

  10. rsync更改端口后的同步办法

    rsync有两种常用的认证方式,一种为rsync-daemon方式,另外一种则是ssh. 在一些场合,使用rsync-daemon方式会比较缺乏灵活性,ssh方式则成为首选.但是今天实际操作的时候发现 ...