Effective Java:类和接口
来源:互联网 发布:动态域名解析软件排名 编辑:程序博客网 时间:2024/06/05 18:54
类和接口是Java程序设计的核心,它们也是Java语言的基本抽象单元。Java语言提供了许多强大的基本元素,供程序员用来设计类和接口。本章阐述的一些指导原则,可以帮助你更好地利用这些元素,设计出更加有用、健壮和灵活的类和接口。
第13条:使类和成员的可访问性最小化
设计良好的模块会隐藏所有的实现细节,把它的API与它的实现清晰地隔离开来。然后,模块之间只通过他们的API进行通信,一个模块不需要知道其他模块的内部工作情况。这个概念被称为信息隐藏或者封装(encapsulation),是软件设计的基本原则之一。
封装能够有效地在各个模块之间解耦合,使得模块能够独立地开发、测试、优化、使用、理解和修改。这样可以加快系统开发的速度,因为这些模块之间依赖很少,能够并行开发。并且减轻了维护负担,在调试它们的时候不影响其他模块。封装还提高了软件的可复用性,因为模块之间依赖很少,除了开发这些模块所使用的环境之外,它们在其他的环境中往往也很有用。最后封装也降低了构建大型系统的风险,因为即使整个系统不可用,但是这些独立的模块却有可能是可用的。
Java提供了许多机制来协助信息隐藏。访问控制机制决定了类、接口和成员的可访问性。实体的可访问性是由该实体声明所在的位置、以及该实体声明中所出现的访问修饰符共同决定的。
规则一:尽可能使每个类或者成员不被外界访问。换言之,应该使用与你正在编写的软件的对应功能相一致的、尽可能最小的访问级别。
如果一个包级私有的顶层类(或者接口)只是在某一个类的内部被用到,就应该考虑使它成为唯一使用它的那个类的私有嵌套类。这样可以将它的访问范围从包中的所有类缩小到了使用它的那个类。
在创建和销毁对象部分中提到了开发中有个实例,我需要为一个创建PDF的方法提供一个现成的对象,创建这个对象的构建器只有在组装这个对象的地方才会用到,因此没有必要像我目前这样,单独声明一个构建器的包级访问类。
如果方法覆盖了超类中的一个方法,子类中的访问级别就不允许低于超类中的访问级别。
如果一个类实现了一个接口,那么接口中所有的类方法在这个类中也都必须声明为公有的,因为接口中的所有方法都隐含着公有访问级别。
实例域决不能是公有的。如果域是非final的,或者是一个指向可变对象的final引用,那么一旦这个域成为公有的,就放弃了对存储在这个域中的值进行限制的能力,这意味着,你也放弃了强制这个域不可变的能力。同时,当这个域被修改的时候,你也是去了对它采取任何行动的能力。因此,包含公有可变域的类并不是线程安全的。即使域是final的,并且引用不可变的对象,当把这个域变成公有的时候,也就放弃了“切换到一种新的内部数据表示法”的灵活性。
同样的建议也适用于静态域,只是有一种例外情况。假设常量构成了类提供的整个抽象中的一部分,可以通过公有的静态final域来暴露这些常量。按惯例,这种域的名称由大写字母组成,单词之间用下划线分开。很重要的一点就是,这些域要么包含基本类型的值,要么包含指向不可变对象的引用。如果final域包含可变对象的引用,它便失去了final域的特性,虽然本身引用不可修改,但是所指向的对象却可以被修改,这个后果比较严重。
长度非0的数组总是可变的,因此类具有公有的静态final数组域,或者返回这种域的访问方法,这几乎总是错误的。如果类具有这样的域或者访问方法,客户端将能够修改数组中的内容。这是安全漏洞的一个常见的根源:
public static final Thing[] VALUES = { ... };
要注意许多IDE会产生返回指向私有数组域的引用的访问方法。修正这个问题有两种方法,一种是使公邮数组变成私有的,并增加一个公有的不可变列表:
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();}
总而言之,你应该始终尽可能地降低可访问性。在设计一个最小的公有API之后,应该防止把任何散乱的类、接口和成员变成API的一部分。除了公有静态final域的特殊情形之外,公有类都不应该包含公有域。并且要确保公有静态final域所引用的对象都是不可变的。
第14条:在公有类中使用访问方法而非公有域
在编写一些bean的时候,我通常的习惯是将所有的属性都私有化,能够提供给外界访问的属性都为其创建公有访问方法——getter和setter:
class Point{ private double x; private double y; public Point(double x, double y){ this.x = x; this.y = y; } public double getX(){return x}; public double getY(){return y}; public void setX(double x){this.x = x;} public void setY(double y){this.y = y;}
如果类可以在它所在的包的外部进行访问,就提供访问方法,以保留将来改变内部表示法的灵活性。如果公有类暴露了它的数据域,想要在将来改变其内部表示法是不可能的,因为公有类的客户端代码已经遍布各处了。
然而,如果类是包级私有的,或者是私有的嵌套类,直接暴露它的数据域并没有本质的错误。比起访问方法的做法,直接访问数据域反而更不会产生视觉混乱。
让公有类直接暴露域虽然不是好的做法,但如果域是不可变的,这种做法的危害就比较小一些。如果不改变类的API,就无法改变这种类的表示法,当域被读取的时候,也无法采取任何辅助行动,但是可以强加约束条件。例如,这个类确保了每个实例都表示一个有效时间:
//Public class with exposed immutable fields —— questionablepublic 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 minute; public Time(int hour, int minute){ if(hour < 0 || hour >= HOURS_PER_DAY) throw new IllegalArgumentException("Hour:" + hour); if(minute < 0 || minute > MINUTES_PER_HOUR) throw new IllegalArgumentException("Min:" + minute); this.hour = hour; this.minute = minute; }}
总之,公有类永远都不应该暴露可变域。暴露不可变域虽然危害比较小,但还是有问题的。有时候会需要用保级私有的或者私有的嵌套类来暴露域,无论这个类是可变的还是不可变的。
第15条:使可变性最小化
第16条:复合优先于继承
第17条:要么为继承而设计,并提供文档说明,要么就禁止继承
第18条:接口优于抽象类
第19条:接口只用于定义类型
第20条:类层次优于标签类
第21条:用函数对象表示策略
第22条:优先考虑静态成员类
- effective java(类和接口)
- Effective Java:类和接口
- effective java-类和接口
- Effective Java: 类和接口
- effective java 类和接口笔记
- [Effective Java]第四章 类和接口
- {Effective Java} Chap 4 类和接口
- Effective-Java-Note-类和接口
- Effective Java 系列-02 类和接口
- Effective Java:类和接口的设计
- 《Effective Java》------类和接口(1)
- 《Effective Java》------类和接口(2)
- Effective Java之类和接口
- effective java之类和接口
- Effective Java读书笔记(4 类和接口)
- Effective Java读书笔记(第4章-类和接口)
- Effective Java——类和接口(上)
- Effective Java——类和接口(下)
- Java抽象类和接口的比较
- Smokeping安装及问题解决
- Problem3-1008
- uboot通过bootargs设置根文件系统的启动位置
- Myelipse 中英文版转换
- Effective Java:类和接口
- Using mutate from dplyr inside a function: getting around non-standard evaluation
- Http请求头和响应头的含义
- MyCat - 源代码篇(16)
- 600个开源iOS应用&库
- 可能忽略的Java基础知识 - 理解内部类和匿名内部类,异常与异常捕获
- 学习EOF
- [转载]ORA-00942 表或视图不存在 问题的解决
- Spark Streaming源码解读之Driver中的ReceiverTracker详解