Effective Java读书笔记--类和接口
1、使类和成员的可访问性最小化
不指定访问级别,就是包私有。protected = 包私有 + 子类
一般private不会被访问到,如果实现了Serializable,可能会泄露。反射。
final集合或者数组,可以返回clone或者使用unmodifiableList等。
java新增2种隐式访问级别,作为模块系统的一部分。一个模块就是一组包。模块内部,可访问性不受导出声明影响,模块中未被到导出的包在模块之外是不可访问的。
2、要在公有类而非公有域中使用访问方法
如果类可以在它所在的包之外进行访问,就提供方法。不要直接访问成员变量,而是通过方法访问。如果是包私有,或者是私有的嵌套类,直接暴露它的数据域并没有本质的错误。
3、使可变性最小化
不可变类:String,基本类型的包装类,BigInteger,BigDecimal.不可变类比可变类更加易于设计、实现和使用。
实现不可变类的5条规则:
1、不要提供任何会修改对象状态的方法。
2、保证类不会被扩展
3、声明所有域都是final
4、声明所有的域都是私有
5、确保对于任何可变组件的互斥访问。在构造器、访问方法和readObject方法中使用保护性拷贝。
纯函数就是不改变入参的值,返回值也不会让外界影响当前对象(比如返回拷贝)。
*一般为了强调这个函数不会改变对象的值,命名一般是介词,或者是动词介词结合。
不可变对象本质上是线程安全的,它们不要求同步。
不可变类可以提供一些静态工厂,把频繁请求的实例缓存起来。
不可变类唯一的缺点是对于每个不同的值都需要一个单独的对象。尤其是大型对象。
防止子类化的方式:本身设置为final;所有构造器都是包私有或者private,然后提供静态工厂方法。
BigInteger,BigDecimal刚被编写出来的时候,对“不可变类必须final”的说话还没被广泛的理解,所以可以被继承,为了保持后向兼容,这个问题无法得到修正。
*稍微弱一点的约束:没有一个方法能够对对象的状态产生外部可见的改变即可。
如果让不可变类实现Serializable接口,并且它包含一个或者或多个指向可变对象的域,就必须显式的readObject或者readResolve方法,或者使用ObjectOutputStream.writeUnshared和ObjectInputStream.readUnshared方法。
如果类不能做成不可变的,仍然应该限制它的可变性。除非有令人信服的理由,否则要使每个域都使private final。
只有在有必要考虑性能的时候,才应该为不可变类提供公有的配套类,比如String的StringBuilder。
不要在构造器或静态工厂方法之外再提供共有的初始化方法,除非有令人信服的理由。
java.util.Date应该是不可变。
4、复合优先于继承(不算接口继承)
在包内部或者使专门为了继承而设计并且具有 很好的文档说明的类来说,继承也是非常安全的。但是跨包边界的继承,则是非常危险的。
继承打破封装性。因为子类得跟着父类的更新而演变。
public class InstrumentedHashSet<E> extends HashSet<E>{
private int addCount = 0;
@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);
}
}
这里调用addAll 会导致addCount 加一次,然后调用到HashSet.addAll(),这里面会调用add方法,从而导致调用被覆写的add方法,addCount 又会加一次。super到父类之后,调用的还是被子类的方法。多态调用的方法得看作用域在哪。
包装类(使用组合去扩展)几乎没缺点,只是不适合用户回调框架。Guava为所有的集合接口提供了转发类。
只有真正是is-a的关系才继承,否则只要不确定是不是,就不应该继承。java有几个不好的实现:stack就不应该继承Vector。properties就不应该继承HashTable。
在复合的地方使用继承会不必要的暴露实现的细节,会永远限定类的性能。暴露细节有可能就会被访问到。
5、要么设计继承并提供文档说明,要么禁止继承。
类必须在文档中说明,在哪些情况它会调用可覆盖的方法。
好的API文档应该是描述一个给定方法做了什么,而不是如何做到。
非API的应该是解释为什么,不是how,也不是what,how看代码。
为了继承而设计的类,唯一的测试方法是编写子类去测试。一般3个大约就可以满足。
*构造器绝不能调用可覆盖的方法。违反可能导致程序失败,因为子类的构造方法调用父类的,父类会调用被覆写的方法,会导致不可预期的结果。构造器调用private,final,static方法是安全的。
如果一个为继承而设计的类实现Cloneable和Serializable接口,就应该意识到clone和readObject方法在行为上非常类似构造器,所以无论clone还是readObject,都不可以调用可覆盖的方法。都是因为覆盖的方法在子类初始化前被运行。
对于那些并非为了安全地进行子类化而设计的和编写文档的类,要禁止子类化。final或者构造器私有。或者不调用可覆盖方法。或者为可覆盖方法编写私有辅助方法。
6、接口优于抽象类
default方法不能覆写Object的方法。
对于骨架实现类而言,好的文档是绝对非常必要的。
7、为后代设计接口
谨慎设计,继承接口,要保证文档说明的行为和所有方法一致,包括继承的缺省方法,否则得覆写。
8、接口只用于定义类型(而不应该用来导出常量)
常量接口模式是对接口的不亮使用(就是接口里放了一堆常量)。代替的方法是使用工具类,或者把常量放到紧密结合的类中。反例:java.io.ObjectStreamConstants,常量类。如果可以用枚举,尽量用枚举。
在数字间使用下划线,是java7开始支持的。
因为接口没法设置final,实现这些接口会被常量污染。
9、类层次(继承)优于标签类(多职责类)
简单来说就是一个类包含过多的标签职责,一个标签就是一个有固定职责的类。
标签类违反了单一职责。
10、静态成员类优于非静态成员类
嵌套类存在的目的应该只是为它的外围类提供服务。如果嵌套类将来可能会用于其他的某个环境中,它就应该是顶层类。
嵌套类:静态成员类,非静态成员类,匿名类,局部类。除了静态成员类,都是内部类。
静态成员类是外围类的一个静态成员,也有可访问性规则。
非静态成员类都隐含地与一个外围实例相关联,这种关联关系不能被修改。可以利用this获得外围实例的引用。通常,当外围类某个实例方法调用非静态成员类的构造器时,这种关联被建立起来。使用实例去调用非静态成员类需要消耗非静态成员类的实例空间,并且增加构造器的时间开销。
非静态成员类的一种常见用法是定义一个Adapter。比如集合的迭代器实现。
private class MyIterator implements Iterator<E>{}
如果声明成员类不要求访问外围实例,就要始终把static放在它的声明中。如果省略static,每个实例都包含一个额外的指向外围对象的引用。保存这份引用要消耗时间和空间(比如对于公共的部分,其实不需要非静态,不需要和外部类关联,非静态会导致每个实例有个单独的成员类关联),会导致外围实例可以回收的时候,却得以保留,导致内存泄漏。
匿名类:同时被声明和实例化。匿名类出现在表达式中,所以要保持简短。
局部类:在任何可以声明局部变量的地方,局部类也遵守作用域的规则。只有局部类在非静态环境才有外围实例,它们也不能包含静态成员。必须简短,否则影响可读性。
11、限制源文件为单个顶级类(一个文件只放一个顶级类)
虽然java编译器允许在一个源文件定义多个顶级类,但是这么做并没有什么好处,只会带来巨大风险。因为如果存在另外一个源文件使用存在一样的顶级类类名,可能会导致程序的结果依赖源文件传给编译器的顺序影响!
实在需要,就把顶级类变成静态成员类。
永远不要把多个顶级类或者接口放在一个源文件中。可以确保编译时,一个类不会有多个定义,也可以保证编译产生的类文件和程序结果不依赖源文件传给编译器时的顺序的影响。
Effective Java读书笔记--类和接口的更多相关文章
- Effective Java2读书笔记-类和接口(一)
第13条:使类和成员的可访问性最小化 设计良好的模块的模块与设计不好的模块区别在于,设计良好的模块会隐藏所有的实现细节,把它的API与他的实现清晰地隔离开来.然后模块之间只通过API通信. 信息隐藏之 ...
- Effective Java2读书笔记-类和接口(四)
第19条:接口只用于定义类型 这一条就举了一个反例,说有些接口中只包含常量.这是对接口的不良使用.要实现相同的功能,应该使用不可实例化的工具类(第4条说过). public class Physica ...
- Effective Java2读书笔记-类和接口(五)
第21条:用函数对象表示策略 这一条其实也没说啥,就是策略模式.碰到这种场景时,定义一个策略接口,然后不同策略子类实现它,主类包含这个接口的引用就可以了. 第22条:优先考虑静态成员类 嵌套类是指被定 ...
- Effective Java2读书笔记-类和接口(三)
第17条:要么为继承而设计,并提供文档说明,要么就禁止继承 第18条:接口优于抽象类 这两条中,提到了一个很重要的概念骨架实现.也就是说,抽象类实现接口的形式.这样的好处是,接口本来不能提供默认的实现 ...
- Effective Java2读书笔记-类和接口(二)
第15条:使可变性最小化 通过一个复数类来看不可变类. public final class Complex { private final double re; private final doub ...
- Effective java读书笔记
2015年进步很小,看的书也不是很多,感觉自己都要废了,2016是沉淀的一年,在这一年中要不断学习.看书,努力提升自己 计在16年要看12本书,主要涉及java基础.Spring研究.java并发.J ...
- Effective Java读书笔记完结啦
Effective Java是一本经典的书, 很实用的Java进阶读物, 提供了各个方面的best practices. 最近终于做完了Effective Java的读书笔记, 发布出来与大家共享. ...
- 【Head First Java 读书笔记】(八)接口与抽象类
接口是什么?它是一种百分之百纯抽象的类. 什么是抽象类?即无法初始化的类. 例如,我们设计一个animal类,以此类为父类,分别设计了多种动物子类,例如Lion,Tiger,Cat,Wolf,Do ...
- Effective Java 读书笔记(一):使用静态工厂方法代替构造器
这是Effective Java第2章提出的第一条建议: 考虑用静态工厂方法代替构造器 此处的静态工厂方法并不是设计模式,主要指static修饰的静态方法,关于static的说明可以参考之前的博文&l ...
随机推荐
- Redis-基础和应用篇
2020,到新公司这一年多以来,更新文章和总结知识的习惯被丢掉了.我复盘了下自己,原因不是公司技术氛围不好,也不是每天业务需求太多,其根本原因还是---惰性.作为我们技术人随着年龄的增长,精力也会被生 ...
- 新建虚拟机ping不通windows主机,windows主机ping不通虚拟机解决办法(图文)
说明: 新建虚拟机和主机互ping不通,因此使用xhell等远程连接工具连接不上 解决办法:配置的时候注意网段 2.修改 /etc/sysconfig/network-scripts/ifcfg- ...
- jdbc事务、连接池概念、c3p0、Driud、JDBC Template、DBUtils
JDBC 事务控制 什么是事务:一个包含多个步骤或者业务操作.如果这个业务或者多个步骤被事务管理,则这多个步骤要么同时成功,要么回滚(多个步骤同时执行失败),这多个步骤是一个整体,不可分割的. 操作: ...
- YGGL.sql
(将表复制粘贴至记事本,再用source命令导入到数据库中) CREATE TABLE `departments` ( `部门编号` char(3) NOT NULL COMMENT '部门编号', ...
- 4.k8s存储之Volume、PV、PVC和StatefulSet
3.Volume 容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题.首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失--容器以干净的状态(镜像最初的 ...
- LeetCode344 反转字符串
编写一个函数,其作用是将输入的字符串反转过来. 示例 1: 输入: "hello" 输出: "olleh" 示例 2: 输入: "A man, a p ...
- JS navigator.userAgent
var u = navigator.userAgent; var isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > - ...
- 基于Docker搭建Hadoop+Hive
为配合生产hadoop使用,在本地搭建测试环境,使用docker环境实现(主要是省事~),拉取阿里云已有hadoop镜像基础上,安装hive组件,参考下面两个专栏文章: 克里斯:基于 Docker 构 ...
- 计算起始车站车费问题-JavaScript数组对象写法
计算起始站车费 题目:深圳--60--广州--50-虎门--40- -中山--36-珠海一34-澳门一89一香港以上车票费用计算,如坐车深圳到广州60元,广州到虎门50元,深圳到虎门就是60+50-1 ...
- 【Nginx】yum安装nginx
这里是nginx的yum安装源: centos7: rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-cent ...