类和接口

13.使类和成员的可访问性最小化

尽可能使每个类或者成员不被外界访问,顶层的类(非嵌套的)和接口,只有俩种可能的访问级别,包级私有的(package-private)和共有的(public)

//公有的,不安全
	public static final Thing[] VALUES = {};
	//把公有数组变成私有的,增加一个公有的不可变数组
	private static final Thing[] PRIVATE_VALUES={};
	public static final List<Thing> VALUES = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
	//把数组变成私有的,添加一个公有方法,返回私有数组的一个备份
	private static final Thing[] PRIVATE_VALUES={};
	public static final Thing[] values(){
		return PRIVATE_VALUES.clone();
	}
14.在共有类中使用访问方法而非公有域
共有类中设置私有域,通过方法获取,也可以强加约束条件
public final class Time {
	private static final int HOURS_PER_DAY = 24;
	private static final int MINUTES_PER_HOUR = 60;
	public final int hour;
	public final int minutes;
	public Time(int hour, int minutes) {
		if(hour <0||hour>=HOURS_PER_DAY){
			throw new IllegalArgumentException("Hour:"+hour);
		}
		if(minutes <0||minutes>=MINUTES_PER_HOUR){
			throw new IllegalArgumentException("minutes:"+minutes);
		}
		this.hour = hour;
		this.minutes = minutes;
	}
}
15.使可变性最小化

不可变类只是它的实例不能被修改的类。

类不可变:

不提供任何会修改对象状态的方法

保证类不会被拓展,防止类子类化,一般的做法是使这个类变成final

使所有的域都是final

使所有的域都是私有的

确保对于任何可变组件的互斥行为,如果类具有指向可变对象的域,则必须确保该类的客户端无法获得指向这些对象的引用

//大部分不可变类使用这种模式,函数的做法,只返回函数的结果
public final class Complex {
	private final double re;
	private final double im;
	public Complex(double re, double im) {
		super();
		this.re = re;
		this.im = im;
	}
	//没有相应的访问器
	public double realPart(){
		return re;
	}
	public double imaginaryPart(){
		return im;
	}
	//这些运算不修改实例
	public Complex add(Complex c){
		return new Complex(re+c.re, im+c.im);
	}
	public Complex subtract(Complex c){
		return new Complex(re-c.re, im-c.im);
	}
	public Complex multiply(Complex c){
		return new Complex(re*c.re-im*c.im, re*c.re+im*c.im);
	}
	public Complex drive(Complex c){
		double tmp = c.re*c.re + c.im * c.im;
		return new Complex((re*c.re+im*c.im)/tmp,(im*c.re-re*c.im)/tmp);
	}
	@Override
	public boolean equals(Object o){
		if(o == this){
			return true;
		}
		if(!(o instanceof Complex)){
			return false;
		}
		Complex c = (Complex) o;
		return Double.compare(re, c.re) == 0&&Double.compare(im, c.im)==0;
	}
	@Override
	public int hashCode(){
		int result = 17 + hashDouble(re);
		result =31 * result +hashDouble(im);
		return result;
	}
	private int hashDouble(double val){
		long longBits = Double.doubleToLongBits(re);
		return (int) (longBits ^(longBits >>>32));
		
	}
	@Override
	public String toString(){
		return "(" + re + "+" + im +")";
	}
}
不可变对象本质上是线程安全的,不需要同步,因为它不会被修改

尽可能的重用现有的实例

public static final Complex ZERO = new Complex(0, 0);
		public static final Complex ONE = new Complex(1, 0);
		public static final Complex I = new Complex(0, 1);
不可变类可以提供一些静态工厂,把频繁访问的数据缓存起来,详见第一条

不可变类的缺点:对于每个不同的值都需要一个单独的对象

如果执行一个多步骤操作,每个操作产生一个新对象,除了最后的,其他的都会被丢弃,性能问题就出来了。

解决方法:猜测会用到哪些多步骤的操作,将他们作为基本类型提供,不可变类就没必要每个步骤单独创建一个对象

无法预测,提供公有的可变配套类,如String类的可变配套类是StringBuilder,和基本上已经废弃的StringBuffer,在特定环境下,相对于BigInteger而言,BigSet也是可变配套类

让不可变的类变成final的另一种做法:

让类的所有构造器都变成私有的或包级私有的,并添加公有的静态工厂来替代公有的构造器(见第一条)

public class Complex1 {
	private final double re;
	private final double im;
	private Complex1(double re, double im) {
		this.re = re;
		this.im = im;
	}
	public static Complex1 valueOf(double re,double im){
		return new Complex1(re, im);
	}
}
静态工厂可以创建方法,工厂的名字表明功能
BigInteger和BigDecimal如果可变,就要进行保护性拷贝
	public static BigInteger safeInstance(BigInteger val){
		if(val.getClass() !=BigInteger.class)
			return new BigInteger(val.toByteArray());
		return val;
	}
为了提高性能,不可变类的规则可以如下:

没有一个方法可以对对象的状态产生外部可见的变化

许多不可变类拥有一个或多个非final的域,第一次请求计算出结果缓存入这些域,再有相同计算,返回缓存的值。

坚决不要为每个get方法编写一个相应的set方法。不要在构造器或者静态工厂之外再提供公有的初始化方法

16.复合优先于继承

继承会导致子类脆弱。

有个方法可以不用拓展现有的类,在新类中增加一个私有域,引用现有类的一个实例,现有类变成新类的一个组件,

//把一个set转变成了另一个set,并增加了计数功能,
public class InstrumentedSet<E> extends ForwardingSet<E> {
	//增加一个 私有域,引用现有类的一个实例,不依赖现有类的实现细节
	private int addCount = 0;

	public InstrumentedSet(Set<E> s) {
		super(s);
	}
	@Override
	public boolean add(E e){
		addCount++;
		return super.add(e);
	}
	@Override
	public boolean addAll(Collection<? extends E> c){
		addCount += c.size();
		return super.addAll(c);
	}
	public int getAddCount(){
		return addCount;
	}
}

public class ForwardingSet<E> implements Set<E> {

	private final Set<E> s;

	public ForwardingSet(Set<E> s) {
		this.s = s;
	}
	public void clear(){
		s.clear();
	}
	public boolean contains(Object o){
		return s.contains(o);
	}
	public boolean isEmpty(){
		return s.isEmpty();
	}
	public int size(){
		return s.size();
	}
	public Iterator<E> iterator(){
		return s.iterator();
	}
	public boolean add(E e){
		return s.add(e);
	}
	public boolean containsAll(Collection<?> c){
		return s.containsAll(c);
	}
	public boolean addAll(Collection<? extends E> c){
		return s.addAll(c);
	}
	public boolean removeAll(Collection<?>c){
		return s.removeAll(c);
	}
	public boolean retainAll(Collection<?>c){
		return s.retainAll(c);
	}
	public Object[] toArray(){
		return s.toArray();
	}
	public <T> T[]toArray(T[] a){
		return s.toArray(a);
	}
	@Override
	public boolean equals(Object o){
		return s.equals(o);
	}
	@Override
	public int hashCode(){
		return s.hashCode();
	}
	@Override
	public String toString(){
		return s.toString();
	}
	@Override
	public boolean remove(Object o) {
		return false;
	}
}
 
基于继承的方法只适用于单个具体的类,并且对于超类中所支持的每个构造器都要求有一个单独的构造器,这里的包装类可以被用来包装任何Set实现,可以结合任何先前的构造器一起工作
	Set<Date> set = new InstrumentedSet<>(new TreeSet<>(cmp));
	Set<E> set2 = new InstrumentedSet<>(new HashSet<>(capacity));
	//替换原本没有计数特性的Set实例
	static void walk(Set<Dog> dogs){
		InstrumentedSet<Dog> iDogs = new InstrumentedSet<>(dogs);
	}
继承违背了封装原则,复合和转发机制代替继承可以避免脆弱性

17.要么为继承而设计,并提供文档说明,要么就禁止继承

为了允许继承,构造器构造器决不能调用可被覆盖的方法

/** 
 * @author 冒云龙 
 * @date 2017年7月13日 下午10:48:06 
 * @describe 
*/

public class Super {

	public Super(){
		overrideMe();
	}
	public void overrideMe(){}
}
public class Sub extends Super{
	private final Date date;
	public Sub() {
		date = new Date();
	}
	@Override
	public void overrideMe(){
		System.out.println(date);
	}
	public static void main(String[] args) {
		//overrideMe方法被super构造器调用时,构造器sub还没有初始化date域
		Sub sub = new Sub();
		sub.overrideMe();
	}
}
18.接口优于抽象类
现有的类可以很容易被更新,以实现新的接口

接口是定义混合类型的理想选择

接口允许我们构造非层次结构的类型框架

public interface Singer {

	AudioClip sing(Song s);
}

public interface Songwritter {

	Song compose(boolean hit);
}

public interface SingerSongWriter extends Singer,Songwritter{

	AudioClip strum();
	void actSensitive();
}

16条包装类模式,接口使得安全地增强类的功能成为可能
通过对导出的每个重要接口都提供一个抽象的骨架实现类,把接口和抽象类的优点结合起来

 

static List<Integer> intArrayAsList(final int[]a){
		if(a==null){
			throw new NullPointerException();
		}
		return new AbstractList<Integer>() {
			public Integer get(int i){
				return a[i];
			}
			@Override
			public Integer set(int i,Integer val){
				int oldVal = a[i];
				a[i]=val;
				return oldVal;
			}
			public int size(){
				return a.length;
			}
		};
	}


 

public abstract class AbstractMapEntry <K,V> implements Map.Entry<K, V>{

	public abstract K getKey();
	public abstract V getValue();
	public V setValue(V value){
		throw new UnsupportedOperationException();
	}
	@Override
	public boolean equals(Object o){
		if(o == this){
			return true;
		}
		if(!(o instanceof Map.Entry))
			return false;
		Map.Entry<?, ?> arg = (Map.Entry) o;
		return equals(getKey(),arg.getKey()) && equals(getValue(),arg.getValue());
	}
	private static boolean equals(Object o1,Object o2){
		return o1==null ?o2==null:o1.equals(o2);
	}
	@Override
	public int hashCode(){
		return hashCode(getKey())^hashCode(getValue());
	}
	public static int hashCode(Object obj){
		return obj == null?0:obj.hashCode();
	}
}


 

19.接口只用于定义类型

常量接口,只包含静态的final域,每个域都导出一个常量,使用这些常量的类实现这个接口,以避免用类名来修饰常量名,反例,不要用,应该用枚举类型(第30条)或者使用不可实例化的工具类,见地4条

public interface PhysicalConstants {

	static final double AAA=1;
	static final double BBB=2;
	static final double CCC=3;
}
工具类通过类名修饰这些常量名,也可以用静态导入

接口应该只被用来定义类型而不是用来导出常量
20.类层次优于标签页

标签类过于冗长,容易出错,效率低下

//标签类,远远低于类层次结构
class Figure {
	enum Shap {RECTANGLE,CIRCLE};
	//标记字段,图的形状
	final Shap shap;
	double length;
	double width;
	//半径
	double radius;
	Figure(double radius) {
		shap = Shap.CIRCLE;
		this.radius = radius; 
	}
	Figure(double length, double width) {
		shap = Shap.RECTANGLE;
		this.length = length;
		this.width = width;
	}
	double area(){
		switch (shap) {
		case RECTANGLE:
			return length * width;
		case CIRCLE:
			return Math.PI * (radius * radius);
		default:
			throw new AssertionError();
		}
	}
}
子类型化,为标签类中的每个方法都定义一个包含抽象方法的抽象类
abstract class Figure1 {
	abstract double area();
}
class Circle extends Figure{
	final double radius;
	Circle(double radius) {
		this.radius = radius;
	}
	double area(){
		return Math.PI * (radius * radius);
	}
}
class Rectangle extends Figure{
	final double length;
	final double width;
	Rectangle(double length,double width) {
		this.length = length;
		this.width = width;
	}
	double area(){
		return length * width;
	}
}
标签类重构到层次中去

21.用函数对象表示策略

22.优先考虑静态成员类

嵌套类是指被定义在另一个类的内部的类,嵌套类存在的目的应该只是为了它的外围类服务

嵌套类有四种:静态成员类,非静态成员类,匿名类,局部类,后三种都成为内部类

静态成员类最好看作是普通的类,只是凑巧声明在一个类的内部,可以访问外围类的所有成员,包括声明为私有的成员,它是外围类的一个静态成员,如果被声明为私有的,就只能在外围类的内部才可以被访问。

如果声明成员类不要求访问外部实例,就要始终把static放在声明中,没有static,则每个实例都包含一个额外的指向外围对象的引用,保存这份引用要消耗时间和空间

匿名类的一种用法是动态的创建函数对象,如根据一组字符串的长度进行排序,另一种用法是创建过程对象,如线程实例,第三种是在静态工厂方法的内部

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值