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 ...
随机推荐
- RewriteCond 和RewriteRule规则说明 (转)
Apache的Mod_rewrite学习 (RewriteCond重写规则的条件)收藏RewriteCond Syntax: RewriteCond TestString CondPattern [f ...
- Let’s Encrypt 通配符证书,泛域名证书申请配置
首先你可以查看下官方提供的支持申请通配符证书的客户端列表:https://letsencrypt.org/docs/client-options/. 参考链接:https://github.com/N ...
- filleSystemBasises
基本查询命令 pwd 查看当前目录 dir 显示当前目录下的文件信息 more 查看文本文件的具体内容 cd 修改用户当前目录 mkdir 创建新的目录 rmdir 删除目录 copy filenam ...
- WebApi 中请求的 JSON 数据字段作为 POST 参数传入
使用 POST 方式请求 JSON 数据到服务器 WebAPI 接口时需要将 JSON 格式封装成数据模型接收参数.即使参数较少,每个接口仍然需要单独创建模型接收.下面方法实现了将 JSON 参数中的 ...
- 记一次flask上传文件返回200前端却504的问题
前言 好久没写了, 主要是太忙了, 本篇记一下今天解决的一个问题吧, 耗了我大半天的时间才解决 问题 今天在调试代码时, 发现了一个诡异的问题, 我之前写了一个接口, 作用是接收上传的文件, 因为这个 ...
- 【JS学习】String基础方法
前言:本博客系列为学习后盾人js教程过程中的记录与产出,如果对你有帮助,欢迎关注,点赞,分享.不足之处也欢迎指正,作者会积极思考与改正. 目录 定义: 字符串的连接: 标签模板的使用: 字符串的基本方 ...
- 【分布式锁的演化】终章!手撸ZK分布式锁!
前言 这应该是分布式锁演化的最后一个章节了,相信很多小伙伴们看完这个章节之后在应对高并发的情况下,如何保证线程安全心里肯定也会有谱了.在实际的项目中也可以参考一下老猫的github上的例子,当然代码没 ...
- Linux 文件查看相关的一些命令
文件压缩解压命令 # 解压 xxx.xz 并删除 xz -d test.tar.xz # 打包成 xxx.tar , 语法: tar -cvf 最后包名.tar ./要打包文件 ./要打包的文件 ta ...
- DHCP最佳实践(三)
这是Windows DHCP最佳实践和技巧的最终指南. 如果您有任何最佳做法或技巧,请在下面的评论中发布它们. 在本指南(三)中,我将分享以下DHCP最佳实践和技巧. 仅在需要时才使用IP冲突检测 运 ...
- 【Oracle】sum(..) over(..)用法分析
今天再看sql优化详解的时候,提到了一个sum(..) over(..) 于是自己实验并在网上找了相关的一些文章来看 下面创建一张表: create sequence xulie increment ...