本篇笔记对应原书条目6,介绍几个避免建立没必要要对象的方法。java
若是一个方法中的一些对象,每次调用都起到相同的做用,那么就能够被重用。程序员
好比,咱们不该该用以下的方式来建立一个String对象:数据库
String str = new String("aaa");
由于当咱们往构造方法里传入aaa的时候,其实这个aaa就是一个String实例了。咱们等因而建立了两个String实例。数组
咱们应该直接这么写:安全
String str = "aaa";
根据jdk文档,上述方式实际上等同于:app
char data[] = {'a', 'a', 'a'}; String str = new String(data);
传入一个字符数组来建立String,避免了建立重复对象。工具
再举一个常见的例子[2],咱们有时但愿遍历一个list,将其中的元素存到一个字符串里,并用逗号分隔。咱们可能会用下面这种最low的办法:性能
public static String listToString(List<String> list) { String str = ""; for (int i = 0; i < list.size(); i++) { str += list.get(i); if (i < list.size() - 1) { str += ","; } } return str; }
这样其实在每次+=
的时候都会从新建立String对象,极大地影响了性能。学习
咱们能够修改一下,采用StringBuilder的方式来拼接list:优化
public static String listToString(List<String> list) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < list.size(); i++) { stringBuilder.append(list.get(i)); if (i < list.size() - 1) { stringBuilder.append(","); } } return stringBuilder.toString(); }
这种方式每次只会生成两个实例——StringBuilder
和最后返回的String
。
那有没有更好的方法呢?咱们能够采用Google Guava的Joiner
,这样每次只用生成一个实例,以下所示:
public static String listToString(List<String> list) { return Joiner.on(",).join(list); }
有时候咱们提供的API中有一些每次调用都具有相同功能的对象,那么就能够把这些对象变成静态的不可变对象,只需实例化一次便可。好比下面这个相似书中的例子[1]:
public static boolean isNumeral(String s) { return s.matches("^[0-9]*$"); }
这个例子用String.matches()
方法来判断字符串是否为数字。每次调用matches()
方法,里面都会建立一个Pattern
对象,这会对性能形成影响。
由于每次调用isNumeral
实际上都会生成一个功能彻底相同的Pattern
对象,因此咱们能够把它抽出来,变成一个静态不可变对象,以下所示:
public static final Pattern NUMBER = Pattern.compile("^[0-9]*$"); public static boolean isNumeral(String s) { return NUMBER.matcher(s).matches(); }
上面咱们谈到了一个不可变对象的重用,接下来咱们再看看可变对象的重用。可变对象的重用能够经过视图(views)来实现。好比,Map的keySet()
方法就会返回Map对象全部key的Set视图。这个视图是可变的,可是当Map对象不变时,在任何地方返回的任何一个keySet都是同样的,当Map对象改变时,全部的keySet也会相应的发生改变。[1]
自动装箱容许程序员混用基本类型和包装类型[1],在二者相计算时,程序会构造出基本类型的包装类型实例。例如,咱们看书中的这个例子:
private static long sum() { Long sum = 0L; for (long i = 0; i <= Integer.MAX_VALUE; i++) sum += i; return sum; }
这个例子里的sum += i
处用Long
型和long
型相加,这样每次都会实例化一个值为i
的包装类型,一共实例化了约231个没必要要的实例,极大地下降了系统的性能。因此咱们在平常开发中,方法内尽可能用基本类型,只在入出参的地方用包装类型。多留心,切忌无心识地使用到自动装箱。[1]
若是涉及到对象池的应用,除非池中的对象很是重,相似数据库链接,不然最好不要去本身维护一个对象池,由于这样会很复杂。另外,有时考虑到系统的安全性,那么咱们须要进行防护性复制,这个在后面会讲到。此时,重复建立对象就是有意义的,由于比起隐含错误和安全漏洞,重复建立对象带来的性能损失是能够接受的。[1]
这个条目要求咱们平时多审视咱们的代码,在能不重复建立对象的地方,就不要重复建立。不要无脑写代码,而是要多留点心。这是一种很是好的编码习惯,也能够为系统带来性能上的优点。
本文仅用于学习交流,请勿用于商业用途。转载请注明出处,若是涉及任何版权问题,请及时与我联系,谢谢!