引用对象的使用和易产生bug的示例
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]已经执行完毕