引用对象的使用和易产生bug的示例

本文属原创,转载请注明出处:http://www.cnblogs.com/robinjava77/p/5481608.html  (Robin)

QuoteTest(引用对象技巧)

  1 import java.util.ArrayList;
  2 import java.util.HashMap;
  3 import java.util.List;
  4 import java.util.Map;
  5
  6 /**
  7  * Created by robin on 2016/4/13.
  8  * 引用型对向操作总结:
  9  * 1.被引用的对象,值改变时,会直接改变引用源的值;
 10  * 2.当引用的对象,改变其引用源时,对其操作,只会改变新的引用源的值,并不会影响之前的引用源的值
 11  * 3.从map中获取的引用不存在时,需要将新的引用put到map中,map中该位置的值,才会被引入
 12  * @author robin
 13  */
 14 public class QuoteTest {
 15
 16     public static void main(String args[]){
 17         Map<String,List<String>> map = new HashMap<String, List<String>>();
 18         for (int i =0;i< 5;i++){
 19             List<String> datalist = new ArrayList<String>();
 20             for (int j=0;j<10;j++){
 21                 datalist.add(i*10+""+j);
 22             }
 23             map.put(""+i,datalist);
 24         }
 25         for (List<String> list:map.values()){
 26             System.out.println(listToString(list));
 27         }
 28         System.out.println("----------分隔线1-----------");
 29         List<String> tempList =  map.get("3");
 30         tempList.add("avc");
 31         tempList.remove("300");
 32         for (List<String> list:map.values()){
 33             System.out.println(listToString(list));
 34         }
 35         System.out.println("----------分隔线2-----------");
 36         List<String> tempList2 =  map.get("2");//tempList 获得map中 key为2的引用
 37         List<String> replaceList = new ArrayList<String>();
 38         tempList2 = replaceList;////tempList 获得其他list的引用,失去map中 key为2的引用,此时对templist2做任何操作,影响的时replaceList引用的区域
 39         tempList2.add("replaceList的值被改变");
 40         for (List<String> list:map.values()){
 41             System.out.println(listToString(list));
 42         }
 43         System.out.println("replaceList的值:"+listToString(replaceList));
 44         System.out.println("----------分隔线3-----------");
 45         List<String> tempList3 =  map.get("2");
 46         tempList3 = replaceList;
 47         map.put("2",tempList3);
 48         for (List<String> list:map.values()){
 49             System.out.println(listToString(list));
 50         }
 51         System.out.println("----------分隔线4-----------");
 52         List<String> notExistList = map.get("5");
 53         if(notExistList == null){
 54             notExistList = new ArrayList<String>();
 55         }
 56         notExistList.add("第5行数据添加进来...");
 57         for (List<String> list:map.values()){
 58             System.out.println(listToString(list));
 59         }
 60         System.out.println("----------分隔线5-----------");
 61         List<String> notExistList2 = map.get("6");
 62         if(notExistList2 == null){
 63             notExistList2 = new ArrayList<String>();
 64         }
 65         notExistList2.add("第6行数据添加进来...");
 66         map.put("6",notExistList2);
 67         for (List<String> list:map.values()){
 68             System.out.println(listToString(list));
 69         }
 70         System.out.println("----------分隔线5-----------");
 71
 72         Map<String,Map<String,String>> mapOne = new HashMap<String, Map<String, String>>();
 73         String keyss = "mapTest";
 74         Map<String,String> mapTwo = new HashMap<String, String>();
 75         mapOne.put(keyss,mapTwo);
 76         System.out.println("mapOne的数据:" + mapToString(mapOne));
 77         System.out.println("----------分隔线6-----------");
 78         mapTwo.put("aaa", "aaav");
 79         mapTwo.put("bbb", "bbbv");
 80         mapTwo.put("ccc","cccv");
 81         System.out.println("mapOne的数据:"+mapToString(mapOne));
 82         System.out.println("----------分隔线7-----------");
 83     }
 84
 85     private static String listToString(List<String> list){
 86         StringBuilder sb = new StringBuilder("");
 87         for (String s:list){
 88             sb.append("["+s+"] ");
 89         }
 90         return sb.toString();
 91     }
 92
 93     private static String mapToString(Map<?,?> map){
 94         StringBuilder sb = new StringBuilder("");
 95         for(Map.Entry entry:map.entrySet()){
 96             sb.append("[key:"+entry.getKey()+";value:"+entry.getValue()+"]");
 97         }
 98         return sb.toString();
 99     }
100
101 }

---------------------

引用对象易产生的bug:

2016.05.11

关于引用对象,使用不恰当,很容易给自己挖坑,产生非常严重的bug,进而导致整个系统实际业务的崩溃,而且这种bug很难被查出来。(如果日志记录不够详细,分析不够彻底,要找出这种bug,只能靠上帝保佑)

下面先上bug 代码 demo

 1 import java.util.Iterator;
 2 import java.util.List;
 3 import java.util.Vector;
 4
 5 /**
 6  * Created by robin on 2016/5/11.
 7  *
 8  * @author robin
 9  */
10 public class QuoteBugDemo {
11
12     private static List<Integer> publicNums = new Vector<Integer>();
13
14     public static void main(String args[]) throws InterruptedException {
15         initPublicNums();//初始化公共数据源
16
17         timeTask(1);//模拟执行定时任务1
18
19         timeTask(2);//模拟执行定时任务2
20
21     }
22
23
24     private static void initPublicNums(){
25         for (int i =0;i < 10;i++){
26             publicNums.add(i);
27         }
28     }
29
30     /**
31      * 这块代码模拟的逻辑:
32      * 1.每天获取配置好10个的数据源;
33      * 2.检查这10个数据源,当数据源的数据准备好后,开始执行数据同步;
34      * 3.从当天的带同步数据源list删除已经同步的数据;
35      * @param mark
36      * @throws InterruptedException
37      */
38     private static void timeTask(int mark) throws InterruptedException {
39         final long start = System.currentTimeMillis();//程序开始运行时间
40         //每天待同步数据源
41         List<Integer> dataSources = publicNums;
42         StringBuffer sb = new StringBuffer("mark("+mark+");公共数据源数目:"+dataSources.size()+";数据源列表[");
43         for (Integer i:dataSources){
44             sb.append(i+",");
45         }
46         sb.append("]");
47         System.out.println("日志【"+sb.toString()+"】");
48         while(true){
49             long seconds = (System.currentTimeMillis() - start) / 1000;
50             if(seconds > 15l){
51                 System.out.println("运行超过限定时间:15秒,退出");
52                 break;
53             }
54             Iterator<Integer> ite = dataSources.iterator();
55             while (ite.hasNext()){//
56                 int dataSource = ite.next();
57                 boolean flag = isOk(dataSource);
58                 if(flag){//数据源数据已准备好
59                     System.out.println("对数据源:"+dataSource+"进行数据处理。");//同步数据
60                     ite.remove();//待同步数据源删除该数据源
61                 }
62             }
63             if(dataSources.size() != 0){
64                 Thread.sleep(1000);
65             }else{
66                 break;
67             }
68         }
69         System.out.println("定时任务mark["+mark+"]已经执行完毕");
70     }
71
72     /**
73      * 模拟检查数据源是否OK
74      * @param dataSource
75      * @return
76      */
77     private static boolean isOk(int dataSource){
78         if(dataSource%2 == 0){
79             return true;
80         }
81         return false;
82     }
83
84
85 }

执行结果:

 1 日志【mark(1);公共数据源数目:10;数据源列表[0,1,2,3,4,5,6,7,8,9,]】
 2 对数据源:0进行数据处理。
 3 对数据源:2进行数据处理。
 4 对数据源:4进行数据处理。
 5 对数据源:6进行数据处理。
 6 对数据源:8进行数据处理。
 7 运行超过限定时间:15秒,退出
 8 定时任务mark[1]已经执行完毕
 9 日志【mark(2);公共数据源数目:5;数据源列表[1,3,5,7,9,]】
10 运行超过限定时间:15秒,退出
11 定时任务mark[2]已经执行完毕

定时任务2,执行的时候,数据源只剩1,3,5,7,9。

改进方案:将公共数据源保护起来,仅提供公共数据源的副本:shallow copy和deep copy

核心代码:

1 /**
2      * 改进方案1:获取公共数据源对象的副本
3      * shallow copy:list中 元素引用 仍然是相同的
4      * @return
5      */
6     private static List<Integer> getPublicNums(){
7         List<Integer> clone = new ArrayList<Integer>(publicNums);
8         return clone;
9     }

改进后全部代码:

 1 import java.util.ArrayList;
 2 import java.util.Iterator;
 3 import java.util.List;
 4 import java.util.Vector;
 5
 6 /**
 7  * Created by robin on 2016/5/11.
 8  *
 9  * @author robin
10  */
11 public class QuoteBugDemo {
12
13     private static List<Integer> publicNums = new Vector<Integer>();
14
15     public static void main(String args[]) throws InterruptedException {
16         initPublicNums();//初始化公共数据源
17
18         timeTask(1);//模拟执行定时任务1
19
20         timeTask(2);//模拟执行定时任务2
21
22     }
23
24
25     private static void initPublicNums(){
26         for (int i =0;i < 10;i++){
27             publicNums.add(i);
28         }
29     }
30
31     /**
32      * 改进方案1:获取公共数据源对象的副本
33      * shallow copy:list中 元素引用 仍然是相同的
34      * @return
35      */
36     private static List<Integer> getPublicNums(){
37         List<Integer> clone = new ArrayList<Integer>(publicNums);
38         return clone;
39     }
40
41     /**
42      * 这块代码模拟的逻辑:
43      * 1.每天获取配置好10个的数据源;
44      * 2.检查这10个数据源,当数据源的数据准备好后,开始执行数据同步;
45      * 3.从当天的带同步数据源list删除已经同步的数据;
46      * @param mark
47      * @throws InterruptedException
48      */
49     private static void timeTask(int mark) throws InterruptedException {
50         final long start = System.currentTimeMillis();//程序开始运行时间
51         //每天待同步数据源
52         List<Integer> dataSources = getPublicNums();//改进方案1
53         StringBuffer sb = new StringBuffer("mark("+mark+");公共数据源数目:"+dataSources.size()+";数据源列表[");
54         for (Integer i:dataSources){
55             sb.append(i+",");
56         }
57         sb.append("]");
58         System.out.println("日志【"+sb.toString()+"】");
59         while(true){
60             long seconds = (System.currentTimeMillis() - start) / 1000;
61             if(seconds > 15l){
62                 System.out.println("运行超过限定时间:15秒,退出");
63                 break;
64             }
65             Iterator<Integer> ite = dataSources.iterator();
66             while (ite.hasNext()){//
67                 int dataSource = ite.next();
68                 boolean flag = isOk(dataSource);
69                 if(flag){//数据源数据已准备好
70                     System.out.println("对数据源:"+dataSource+"进行数据处理。");//同步数据
71                     ite.remove();//待同步数据源删除该数据源
72                 }
73             }
74             if(dataSources.size() != 0){
75                 Thread.sleep(1000);
76             }else{
77                 break;
78             }
79         }
80         System.out.println("定时任务mark["+mark+"]已经执行完毕");
81     }
82
83     /**
84      * 模拟检查数据源是否OK
85      * @param dataSource
86      * @return
87      */
88     private static boolean isOk(int dataSource){
89         if(dataSource%2 == 0){
90             return true;
91         }
92         return false;
93     }
94
95
96 }

执行结果:

 1 日志【mark(1);公共数据源数目:10;数据源列表[0,1,2,3,4,5,6,7,8,9,]】
 2 对数据源:0进行数据处理。
 3 对数据源:2进行数据处理。
 4 对数据源:4进行数据处理。
 5 对数据源:6进行数据处理。
 6 对数据源:8进行数据处理。
 7 运行超过限定时间:15秒,退出
 8 定时任务mark[1]已经执行完毕
 9 日志【mark(2);公共数据源数目:10;数据源列表[0,1,2,3,4,5,6,7,8,9,]】
10 对数据源:0进行数据处理。
11 对数据源:2进行数据处理。
12 对数据源:4进行数据处理。
13 对数据源:6进行数据处理。
14 对数据源:8进行数据处理。
15 运行超过限定时间:15秒,退出
16 定时任务mark[2]已经执行完毕

已达预期。

------------------------------------------

补充:shallow copy 和 deep copy

------------------------------------------

时间: 2024-07-29 19:26:35

引用对象的使用和易产生bug的示例的相关文章

JS 内存堆栈原理(注销引用对象后带来的问题)

var a = { name: 123}; var b = a; a.name; 123 b.name; 123 a.name = 'wolf'; "wolf" b.name "wolf" a.name = null; //注销此引用对象的内存,引用此引用对象的对象,失去对引用对象的引用,获得属于自己的引用对象副本; null b.name null a = null; null b Object {name: null}

设计一个不强引用对象的单例字典

大家都知道,使用NSDictionary存储对象的时候会强引用对象,导致被存储对象的引用计数+1,有时候,我们想用单例来存储对象,但又不希望强引用存储的对象,这该怎么实现呢? 在这里,我们可以使用NSMapTable来实现这个功能. 我直接给出源码: WeakDictionary.h   +   WeakDictionary.m // // WeakDictionary.h // 弱引用字典 // // http://www.cnblogs.com/YouXianMing/ // Copyrig

Andorid Binder进程间通信---Binder本地对象,实体对象,引用对象,代理对象的引用计数

本文参考<Android系统源代码情景分析>,作者罗升阳. 一.Binder库(libbinder)代码: ~/Android/frameworks/base/libs/binder ----BpBinder.cpp ----Parcel.cpp ----ProcessState.cpp ----Binder.cpp ----IInterface.cpp ----IPCThreadState.cpp ----IServiceManager.cpp ----Static.cpp ~/Androi

String,Array等原生引用对象prototype问题(打X未解决,打勾已解决)

1.2014年6月29日 10:51:44    原型模式     疑问一:    String类型中,动态为其加入函数时候,可以通过String.prototype.[[functionname]] = function(){}方式或者直接string.[[functionname]] = function(){},但是遇到了一个问题,当创建一个实例时,只有前者可以运行,后者报错找不到该方法,如:var msg = 'hello world' ; msg.[[functionname]] ; 

引用对象做向上转换,但对虚函数不影响使用

//: WIND2.CPP -- Inheritance & upcasting #include <iostream.h> enum note { middleC, Csharp, Cflat }; // Etc. class instrument { public: void play(note) const { // 不是虚函数 cout << "instrument::play" << endl; } }; class wind :

第52条:通过接口引用对象

应该优先使用接口而不是类来引用对象,考虑Vector的情况. List<Subscriber> subscribers = new Vector<Subscriber>(); Vector<Subscriber> subscribers = new Vector<Subscriber>(); 应该使用第一种方式,如果使用接口作为类型,程序将会更加灵活,当决定更换实现时,只需改变构造器中的类的名称(或者使用一个不同的静态工厂). 注意,如果原来的实现提供了某种

以引用对象代替单例模式

介绍: 系统中存在单例的全局訪问点,你希望将对单例的訪问通过对象引用来实现.往往是将对单例的依赖关系转换为关联关系. 动机: 在系统中引入单例模式往往并没有起到明显的效果却添加了系统的复杂性.不能只由于某个类只须要一个实例而採用单例模式.这些全然能够用引用对象代替. 通过全局訪问点使用单例对象往往造成依赖不清.可读性差等问题,我们全然能够通过显式的关联引用来做到在子系统中共享同一个实例,而且仅仅对须要这个实例的对象注入依赖.将对全局变量的依赖转变为对成员对象的依赖使类更易于理解. 当设计须要中不

父类的引用对象指向子类的对象

在java的多态中,经常会看到父类的引用对象指向子类的对象,一开始很迷茫,因为按照之前所学的知识,父类的引用对象指向自身对象或者子类的引用对象指向自身对象都不难理解,因此为了方便理解,下面举了一个例子:水杯和水壶的比喻. 下面的一段代码来实现这个比喻: 1 public class Kettle { 2 public Kettle(){ 3 System.out.println("水壶容量有2升"); 4 } 5 } 6 7 public class Cup extends Kettl

javascript中的循环引用对象处理

先说明一下什么是循环引用对象: var a={"name":"zzz"}; var b={"name":"vvv"}; a.child=b; b.parent=a; 这里的a和b都是一个循环引用对象. 循环引用对象本来没有什么问题,序列化的时候才会发生问题,比如调用JSON.stringify()对该类对象进行序列化,就会报错: Converting circular structure to JSON.    而序列化需求很