使类和成员的可访问能力最小化
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Test {
public static void main(String[] args) {
for (int i = 0; i < foo.VALUES.length; i++) {
foo.VALUES[i] = new Integer(0);
}
for (int i = 0; i < foo.VALUES.length; i++) {
System.out.println(foo.VALUES[i]);
}
}
}
class foo {
public static final Integer[] VALUES = { new Integer(0), new Integer(1),
new Integer(2), new Integer(3) };
}
虽然VALUES被定义成final,但是用户在外部还是能修改数组中的值,这是很不安全的。
如下代码是推荐做法,公有数组应该被替换为一个私有数组,以及一个公有的非可变列表。
private static final Integer[] PRIVATE_VALUES = {new Integer(0),new Integer(1), new Integer(2),new Integer(3)};
public static final List VALUES = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
也可以把公有数组替换成一个公有方法,它返回私有数组的一份拷贝。
public static final Integer[] VALUES() {
return (Integer[])PRIVATE_VALUES.clone();
}
非可变类
非可变类创建和使用都比较简单,它也是线程安全的。但它有个缺点需要注意:对于每一个不同的值都要求一个单独的对象。这点很重要,关乎程序的执行效率。比如通过大量的String去生成另一个String是不太划算的,因为除了最后的结果,中间的其他对象都会被丢弃,所以应该考虑用字符串变量StringBuffer。
让一个非可变类不能被继承有三种方法:
- 将类声明成final
- 将类的每一个方法声明成final
- 将构造函数声明成private,另提供一个静态工厂方法。(推荐)
为什么非可变类要设计成不能继承了,可以看一下这个讨论:http://stackoverflow.com/questions/2068804/why-is-string-final-in-java
我的理解是,如果非可变类A支持继承,那么子类可能实现为可变类B,会增加使用上的复杂性。
比如有个拷贝函数形参是非可变类A,但我不知道传入的是A还是B,我们还要通过判断才能决定采用浅拷贝还是深拷贝。
组合优于继承
想想如下的程序输出是6,而不是3,为什么?
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
public class Test {
public static void main(String[] args) {
MyHashSet mhs = new MyHashSet();
mhs.addAll(Arrays.asList(new String[] { "a", "b", "c" }));
System.out.println(mhs.getSum());
}
}
class MyHashSet extends HashSet {
private int sum = 0;
public boolean add(Object arg0) {
sum++;
return super.add(arg0);
}
public boolean addAll(Collection arg0) {
sum += arg0.size();
return super.addAll(arg0);
}
public int getSum() {
return sum;
}
}
因为HashSet的addAll会调用add实现,所以在addAll中,sum加了3,并且有调用了add三次。
从这个例子可以看出,继承时,子类的行为还要受超类的实现的影响。当一个程序员同时维护超类和子类时,这不会有什么问题,但是实现的子类依赖第三方发布包的时候呢?
实现超类时,构造函数中如果有调用其他方法,那么这些方法建议声明成final,防止子类改写。
接口
接口优于抽象类,接口只是被用于定义类型。
常量接口模式是对接口的不良使用,接口应该只是被用来定义类型的,它们不应该用来导出常量。如果要导出常量时,应该用一个不可实例化的工具类:
public class Constants {
private Constants() {
}
public static final int A = 1;
public static final int B = 2;
public static final int C = 3;
public static final int D = 4;
}
嵌套类
嵌套类有四种:静态成员类、非静态成员类、匿名类和局部类。
如下代码展示了静态成员类和非静态成员类的使用。
如果你声明的成员类不要求访问外围实例,那么请记住把static修饰符放到成员类声明中。否则,每个实例都将包含一个额外的指向外围对象的引用,维护这份引用一消耗时间和空间,但又没有相应的好处。
public class Test {
public static void main(String args[]) {
Human mike = new Human("mike");
Human.Cup mikeCup = mike.new Cup(); // 非static的成员类,实例化时要依赖于外部类的实例。
System.out.println(mikeCup.whoseCup());
Human.Car joyCar = new Human.Car("joy"); // static的成员类,实例化时不依赖于外部类的实例。
System.out.println(joyCar.whoseCar());
}
}
class Human {
private String name;
public Human(String name) {
this.setName(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public class Cup {
String whoseCup() {
return getName(); // ok
}
}
public static class Car {
private String ownerName;
Car(String name) {
this.ownerName = name;
}
String whoseCar() {
// return getName(); //error...staitc的静态类不能访问外部类的非static成员。
return this.ownerName;
}
}
}
而局部类和匿名类是针对方法而言的,如果你只需要在一个地方创建它的实例,并且已经有了一个预先存在的类型可以说明这个类的特征,则把它做成匿名类,否则就做成局部类。
如下代码展示了匿名类的两个常用用法:
import java.util.Arrays;
import java.util.Comparator;
public class Test {
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
int i = 0;
while (true) {
i++;
System.out.println("thread i = " + i);
if (100 == i) {
break;
}
}
}
}).start();
Integer[] lst = { new Integer(5), new Integer(3), new Integer(2),
new Integer(1) };
Arrays.sort(lst, new Comparator() {
public int compare(Object x, Object y) {
return ((Integer) x).intValue() - ((Integer) y).intValue();
}
});
for (Integer i : lst) {
System.out.println(i);
}
}
}
来源:oschina
链接:https://my.oschina.net/u/1453800/blog/232712