《Effective Java读书笔记》--类和接口

Deadly 提交于 2020-02-28 11:42:57

使类和成员的可访问能力最小化

应该尽可能使每一个类或成员不被外界访问。
如果一个包级私有的顶层类或者接口只是在某一个类的内部被调用到,应该考虑使用嵌套类实现。
确保public staitc final所引用的对象是不可变的。
public static final的数组几乎总是错误的:
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);
		}
	}
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!