前言:
本文主要都是一些概念性的阅读理解,本来考虑了好久要不要写这篇文章,但是考虑到读书笔记的完整性,还是决定写完。仅仅是对书中概念的一个经验性的解答。
第50条 如果其他类型更合适,则尽量避免使用字符串;
书中提及几条:1字符串不适合代替其他的值类型;2字符串不适合代替枚举类型;3字符串不适合代替聚集类型;4字符串不适合代替能力表;
一开始读这几条黑体我是比较懵的。后来工作中慢慢的用到一些东西,才慢慢了解这几条的意义。这里简单就这几条讲一下。如果已经理解,请忽略。
1字符串不适合代替其他的值类型;
从网络中获取的数据,例如我们从服务端接口获取数据,现在通常会返回json格式数据。很多时候,都是字符串形式表示的。
解析出来之后也是字符串的 Key 和Value的键值对。这样就造成了许多该用boolean值的地方用了true和false的字符串去比较。
2字符串不适合代替枚举类型;
枚举类型比字符串更适合表示枚举类型的常量。关键就是枚举的好处,比较的时候,字符串比较远不如枚举的比较。枚举类型可以采用集合类型的遍历等方式。
3字符串不适合代替聚集类型;
聚集类型这个概念我的理解是通过一个字符串的命名来区分不同的类型操作。例如CaculatorAdd,CaculatorSub等,通过这种命名区分不同的操作。每次都需要先去解析字符串。再拿到操作方式Add,Sub。当然这种方式是低效的。解析字符串,再通过字符串匹配是一个易错低效的操作。
4字符串不适合代替能力表;
能力表这个概念也是一个比较尴尬的概念。书中是通过ThreadLocal的不可伪造的键(某个线程的唯一标志)来说明。用某个字符串作为唯一的键,有可能在命名的时候恰巧相同导致异常发生。
第51条 :当心字符串连接的性能
代码一:
@Test
public void test() {
long currentTime = System.currentTimeMillis();
String result = "";
// System.out.println(Integer.MAX_VALUE);
for (int i = 0; i < 10000; i++) {
result = result+i;
}
long lastTime = System.currentTimeMillis();
System.out.println("耗时"+(lastTime - currentTime));
}
@Test
public void test2() {
long currentTime = System.currentTimeMillis();
StringBuilder result = new StringBuilder("");
// System.out.println(Integer.MAX_VALUE);
for (int i = 0; i < 10000; i++) {
result = result.append(i);
}
long lastTime = System.currentTimeMillis();
System.out.println("耗时"+(lastTime - currentTime));
}
执行结果1:
耗时381
执行结果2:
耗时1
差距不要太大。
主要是String和StringBuilder(或者StringBuffer)的区别要好好理解。
可以参考下面这个我之前给其他人的一个回答:
第一个问题:既然StringBuffer与String同样为引用数据类型,StringBuffer设计的意义何在?
首先说,String 类:不可变类的概念一定要懂。也就是一旦创建就不可变。例如,下面这个例子
String str = "" ;
for (int i = 0; i < 100; i++) {
str = str +i;
}
但是这样做有每次执行这行代码str=str +i都会新建一个String对象,然后对str赋值。现在数量是100次,可能还看不出这个新建对象的开销的可怕,如果是100万个呢?
所以有了StringBuffer,(ps:虽然现在大家都用StringBuilder),如下代码:
StringBuilder sBuilder = new StringBuilder("");
for (int i = 0; i < 100; i++) {
sBuilder = sBuilder.append(i);
}
结果还是一摸一样。但是,至始至终,只有一个StringBuilder对象。那么可想而知,不管执行多少次,也只有一次新建对象的开销。
第二个问题:至于为什么要将StringBuffer设计为final类?
原因就是不想让你继承它。因为设计它的唯一目的就是为了辅助String类处理上面说的这种开销。他是一个辅助类。
第三个问题:若是要通过继承扩展功能怎么办?
如果要扩展,那么只能通过包装的方式,也就是我们说的装饰者模式。举个例子如下:
public class MyStringBuilder {
private StringBuilder strb ;
private static int count = 0;
public MyStringBuilder(StringBuilder strb) {
super();
this.strb = strb;
}
//例如:新增一个添加方法的计数器
public void append(String str){
strb.append(str);
count++;
}
}
第52条 通过接口使用对象
比如集合类Collection和子类的,Map和子类的关系要了解清楚。当能通过使用超类或者接口,且超类或者接口的方法能满足需求的时候,尽量使用超类或者接口声明实例。
举个例子:
android写网络框架的时候,都要处理成功success的操作,failed的操作。那么我写一个接口NetUtll包含success,failed两个方法。以后如果替换了网络实现也可以继续使用。对后续代码的扩展性大大加强。
第53条:接口优先于反射机制
几乎都是因为反射自身的缺点导致:例如编译时检查不出调用了不存在的方法,会运行时失败。执行反射的代码性能损失。反射的代码也会造成阅读困难,毕竟谁知道你反射的是哪个类的哪个方法,和其他代码几乎无耦合性。
当然,反射也是存在很多优点的,例如解耦合,例如hook其他类的方法等。
第54条:谨慎的使用本地方法
略,当前几乎用不到本地方法。
第55条:谨慎的进行优化
略,不要因为优雅的代码阅读去优化。在没有数据支撑的前提下,也不要为了追求所谓可能存在的性能提高而去优化。
第56条:遵守普遍接受的命名习惯
略,命名习惯大多都是约定俗成的。