avatar

正进一步

只有喜欢,才会全情投入,才会成功!!!

不关注架构设计细节的程序员不是一个好架构师

  • Home
  • Tags
  • Categories
  • Archives
  • 2018寄语
effective_52通过接口引用对象

尽量使用接口引用对象

不用接口引用的情况:

没有合适的接口的情况。例如: 值类型,String和StringBuilder,Random。通常是final类型.
如果程序依赖于实现了某个接口的类的额外方法,例如LinkedHashMap

effective_51_当心字符串连接的性能

不要使用字符串连接操作符来合并多个字符串,除非性能无关紧要。相反,应该使用StringBuilder的append方法。另一种方法是,使用字符数组,或者每次只处理一个字符串,而不是将它们组合起来。
以为字符串的不可变,当两个字符串连接在一起时,他们的内容就会被拷贝

1
2
3
4
5
StringBuilder b = new StringBuilder(numItems() * LINE_WIDTH);
for(int i = 0; i < numItem(); i++){
b.append(lineForItem(i));
}
return b.toString();
effective_49基本类型优先于装箱基本类型

知识:

当程序进行涉及到装箱和拆箱基本类型和混合类型计算时,会进行拆箱

基本类型和装箱基本类型之间的三个主要区别:

1.基本类型只有值,而装箱基本类型具有与它们的值不同的同一性(两个装箱基本类型可以具有相同的值和不同的同一性)

2.基本类型只有功能完备的值,而每个装箱基本类型除了它对应的基本类型的所有功能值之外,还有个非功能值:null

3.基本类型通常比装箱基本类型更节省空间和时间。

适合使用装箱基本类型的地方:

1.作为集合中的元素,键和值。

2.在参数化类型中,必须使用装箱基本类型作为类型参数。

3.在进行反射方法调用时,必须使用装箱基本类型。

effective_46_foreach循环优先于传统的for循环

foreach可以遍历iterable接口
foreach循环在简洁性与预防bug方面有着传统的for循环无法比拟的优势.

除了一下情况:

过滤- 将删除特定元素remove方法
转换- 使用列表迭代器或者数组索引,以便设定元素的值
平行迭代- 如果并行遍历多个集合,就需要显示的控制迭代器和索引变量

effective_45_将局部变量的作用域最小化

http://blog.csdn.net/zhtzyh2012/article/details/46675065

循环提供特殊机会将变量作用域最小化.(无论传统for,还foreach).for循环,都允许声明循环变量,它们的作用域被限定在正好的作用范围之内.(范围包括循环体,以及循环体之前的初始化,测试,更新).如果循环终止之后不再需要循环变量内容,for循环优于while循环.
几乎每个局部变量的声明都应该包含一个初始化表达式.如果没有足够信息来对一个变量进行有意义的初始化,就应该推迟这个声明,直到可以初始化为止.例外情况是与try..catch有关.
for循环比while循环另外优势:更简短,增加可读性.

1
2
3
4
5
6
7
8
9
10
public static void main(String[] args) {
Class<?>cl = null;
try{
cl= Class.forName(args[0]);
}catch (ClassNotFoundException e){
System.err.println("Classnot find");
System.exit(1);
}

}
43返回零长度的数组或集合,而不是null

Collections.emptyList().toArray(new Person[0]);

此处可以参考阿里Java编程规范手册

使用toArray带参方法,入参分配的数组空间不够大时,toArray方法内部将重新分配内存空间,并返回新数组地址;如果数组元素大于实际所需,下标为[ list.size() ]的数组元素将被置为null,其它数组元素保持原值,因此最好将方法入参数组大小定义与集合元素个数一致。

Collections.emptyList()

33用EnumMap代替序数索引

最好不要用序数来索引数组,而要使用EnumMap

EnumMap源代码学习

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 public V put(K key, V value) {
typeCheck(key);

int index = key.ordinal();
Object oldValue = vals[index];
vals[index] = maskNull(value);
if (oldValue == null)
size++;
return unmaskNull(oldValue);
}

public V remove(Object key) {
if (!isValidKey(key))
return null;
int index = ((Enum<?>)key).ordinal();
Object oldValue = vals[index];
vals[index] = null;
if (oldValue != null)
size--;
return unmaskNull(oldValue);
}

public V get(Object key) {
return (isValidKey(key) ?
unmaskNull(vals[((Enum<?>)key).ordinal()]) : null);
}

public boolean containsKey(Object key) {
return isValidKey(key) && vals[((Enum<?>)key).ordinal()] != null;
}

21用函数对象表示策略

实现函数对象的几种方式:

  • C语言的函数指针
  • lambda表达式
  • 函数式接口

函数对象主要用于策略模式.只用一次的话,使用匿名内部类;多次使用的话,可以导出成一个成员域(static final修饰)

实例:
java.util.Comparator

21用函数对象表示策略

实现函数对象的几种方式:

  • C语言的函数指针
  • lambda表达式
  • 函数式接口

函数对象主要用于策略模式.只用一次的话,使用匿名内部类;多次使用的话,可以导出成一个成员域(static final修饰)

实例:
java.util.Comparator

19接口只用于定义类型

当类实现接口时 ,接口就充当可以引用这个类的实例的类型(type)。因此,类实现了接口,就表明客户端可以对这个类的实例实施某些动作。为了任何其他目的而定义接口是不恰当的。

有一种接口被称为常量接口(constant interface),它不满足上述条件。这种接口没有方法,只有静态final域。常量接口模式是对接口的不良使用。这样做不仅会暴露该类的实现细节到导出API中,还会产生一个更糟糕的情况。就是在将来的发行版中,如果类不再需要使用这些常量,它依然必须实现这个接口,以确保二进制兼容性。如果非final类实现了常量接口,它的所有子类的命名空间也会被接口中的常量所“污染”。

反例

Java平台类库中有几个常量接口,例如java.io.ObjectStreamConstants。这些接口应该被认为是反面的典型,不值得效仿。

方案。

1.如果常量与某个现有类或接口紧密相关,就应该把这些常量添加到这个类或接口中。

2.如果这些常量可以被看作枚举类型的成员,就应该用枚举类型(enum type)。

3.用不可实例化的工具类( utility class)。使用静态导入(import static)避免常量修饰.

慎用可变参数
  • 至少传递一个
1
2
3
4
5
private static <E> void varargs(Object obj,E...varargs){
for(E e : varargs){
System.out.println(e);
}
}
  • 泛型编程只能接受引用类型

  • 打印数组tostring,会导致没有意义的字符结果

  • 可变参数每次调用都会导致有一次数组分配和初始化

  • EnumSet静态工厂最大限度地减少创建枚举集合的成本

1
2
3
4
5
6
7
public static <E extends Enum<E>> EnumSet<E> of(E first, E... rest) {
EnumSet<E> result = noneOf(first.getDeclaringClass());
result.add(first);
for (E e : rest)
result.add(e);
return result;
}
  • Arrays.toString(int[] args)可以打印数组,而不是可变参数
优先使用Lamdba,而不是匿名内部类
  • Lambda没有文档和名字,如果不能方法体太长或者不能自解释的话,那么就不要放到lamdba中。

  • Lambda不能使用this访问自身。this在Lambda是访问的外部类。如果要在方法体中访问自身的话,那么就不能使用Lambda

  • 不要在创建函数对象的时候使用匿名内部类,除非创建的对象不是函数式接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public enum Operation {
PLUS ("+", (x, y) -> x + y),
MINUS ("-", (x, y) -> x - y),
TIMES ("*", (x, y) -> x * y),
DIVIDE("/", (x, y) -> x / y);
private final String symbol;
private final DoubleBinaryOperator op;
Operation(String symbol, DoubleBinaryOperator op) {
this.symbol = symbol;
this.op = op;
}
@Override public String toString() { return symbol; }
public double apply(double x, double y) {
return op.applyAsDouble(x, y);
}
}