类和接口
第十三条使类和成员的可访问性最小化
应该使用与你正在编写的软件的对应功能相一致的、最小的访问级别
对于顶层的类(顶层是指非嵌套的类)和接口只有两种可能的访问级别:包级私有和公有的。
如果类或接口可以被做成包级私有的就应该做成包级私有的,这样这个类实际成了这个包的实现的一部分,可以在以后对他修改,替换而不用担心影响到客户端程序,如果做成公有的,你就有责任永远支持它的兼容性。
如果一个包级私有的顶层类(或接口)只是在某一个类的内部被用到,就应该使他成为唯一使用它的那个类的私有嵌套类。这样可将该类的可访问范围降到使用它的那个类。
对于成员(域,方法,嵌套类和嵌套接口)有四种可能的访问级别
私有的(private),包级私有的(package-private,缺省值),受保护的(protected),公有的(public)
私有成员和包级成员都是一个类的实现中的一部分,一般不会影响它的导出的API,然而,如果这个类实现了Serializable接口(74,75条),这些域就有可能会被“泄漏”(leak)到导出的类
对于公有类的成员,当访问级别从包级私有变成保护级别时,会大大增强可访问性。(因为)受保护的成员是类的导出的API的一部分,必须永远得到支持。受保护的成员应该尽量少用。
如果方法覆盖了超类中的一个方法,子类中的访问级别就不允许低于超类中的访问级别,这样可以确保任何可使用超类的实例的地方也都可以使用子类的实例。
实例域绝不能是公有的,如果域是非final的,或者是一个指向可变对象的final引用,那么一旦使这个域成为公有的,就放弃了对存储在这个域中的值进行限制的能力。
同样的建议也适用于静态域,除非是:常量构成了类提供的整个抽象中的一部分,可以通过公有的静态final域来暴露这些常量,且这些域要么包含基本类型的值要么包含指向不可变对象的引用。
长度非零的数组总是可变的.所以这种情况总是错误的:类具有公有的静态final数组域,或者返回这种域的访问方法。
比如有:
public static final Thing[] VALUES = {...};
这往往会是安全漏洞(security hole)
但使用这个类的对象又要获取这个VALUES怎么办呢,两种方法:
private static final Thing[] PRIVATE_VALUES = {。。。};
public static final List<Thing> =
Collections.unmodifiableList(ArrayList.asList(PRIVATE_VALUES));
第二种:
private static final Thing[] PRIVATE_VALUES = {。。。};
punlic static final Thing[] values(){
return PRIVATE_VALUES.clone();
}
总之:你再仔细地设计了一个最小地公有API之后,应该防止把任何散乱的类、接口和成员变成API的一部分。除了公有静态final域的特殊情形之外,公有类都不应该包含任何公有域。并且要确保公有静态final域所引用的对象都是不可变的。
第十四条在公有类中使用访问方法而非公有域
1.公有类永远都不应该暴露可变的域
2.虽然还是有问题,但是让公有类暴露不可变的域其危害比较小。
3.然而如果类是包级私有的,或者私有的嵌套类,直接暴露它的数据域并没有本质的错误。(这种做法相比于访问方法更不会产生视觉混乱。而且影响上说:虽然客户端代码与该类的内部表示法紧密相连,但是这些代码被限定在包含该类的包中。如有必要,不改变包之外的任何代码而只改变内部数据表示法也是可以的。在私有嵌套类的情况下,改变的作用范围被进一步限制在外围类中)
第十五条使可变性最小化
为了使类成为不可变类,要遵循下面五条规则:
1.不要提供任何会修改对象状态的方法(也称为mutator)
2.保证类不会被扩展。这样可以防止粗心或者恶意的子类假装对象的状态已经改变,从而破坏该类的不可变行为(????),为了防止子类化,一般的做法是使这个类成为final的,但是后面我们还会讨论到其他方法。
3.使所有域都是final的。通过系统的强制方式,这可以清楚地表明你的意图。而且,如果一个指向新创建实例地引用在缺乏同步机制地情况下,从一个线程被传递到另一个线程,就必须确保正确地行为,正如内存模型中所述。[深入理解Java内存模型(六)——final](http://ifeve.com/java-memory-model/)
4.使所有域都成为私有的。虽然从技术上将,将基本类型或指向不可变对象地final域设为public也没有问题,但是不建议这样做,因为这样你以后就不能更改其内部表示了(要保持兼容性)
5.确保对于任何可变组件的互斥访问。这里的互斥指的是这个类和其他类对可变组件的互斥访问,跟具体的说就是,不要让其他类拿到或拥有这个类中的任何可变组件的引用。如果要客户端提供的对象来初始化某个指向可变组件的域时,使用保护性拷贝。
下面是一个例子:
package org.effectivejava.examples.chapter04.item15;
public final class Complex {
private final double re;
private final double im;
public Complex (double re, double im) {
this .re = re;
this .im = 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 );
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.im + im * c.re);
}
public Complex divide (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 + "i)" ;
}
}
这个类表示一个复数,注意这些算数运算是如何创建并返回Complex实例,而不是修改这个实例(虽然想修改也修改不了)。大多数重要的不可变类都使用可这种模式。他被称为函数式的做法(无副作用)。与之对应更常见的是过程式的和命令式的。
不可变类的优势与用法:
1.简单
2.线程安全,不需要同步
3.不可变类可以被自由地共享--->不需要clone方法或拷贝构造器(11条)
4.甚至可以共享它们的内部信息
5.不可变对象为其他对象提供了大量的构件(building blocks)????
不可变类的缺点:
对于每个不同的值都需要一个单独的对象。
解决方法(优化方法):
1.先猜测一下会经常用到哪些多步骤的操作,然后将它们作为基本类型提供,不可变的类可以不必在每个步骤单独创建一个对象。
不可变的类在内部可以更加灵活。例如,BigInteger有一个包级私有的可变“配套类(companing class)”,它的用途是加速诸如“模指数”这样的多步骤操作。
下面是BigInteger中的模指数代码
/**
* Returns a BigInteger whose value is
* <tt>(this<sup>exponent</sup> mod m)</tt>. (Unlike {@code pow}, this
* method permits negative exponents.)
*
* @param exponent the exponent.
* @param m the modulus.
* @return <tt>this<sup>exponent</sup> mod m</tt>
* @throws ArithmeticException {@code m} ≤ 0 or the exponent is
* negative and this BigInteger is not <i>relatively
* prime</i> to {@code m}.
* @see #modInverse
*/
public BigInteger modPow (BigInteger exponent, BigInteger m) {
if (m.signum <= 0 )
throw new ArithmeticException("BigInteger: modulus not positive"</