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 ...
随机推荐
- Alpha冲刺——汇总博客
一.代码规范与计划 代码规范与计划 二.10篇冲刺随笔 冲刺随笔--Day1 冲刺随笔--Day2 冲刺随笔--Day3 冲刺随笔--Day4 冲刺随笔--Day5 冲刺随笔--Day6 冲刺随笔-- ...
- Spark的dataframe转rdd通用工具类
需求解决问题 当每次读取hive表或者其他数据源,获取数据,相对其进行rdd操作,遇到任何类都需要df.rdd(row>row.getstring(0))去获取,就很麻烦,所以可以实现个通用的转 ...
- Blazor VS 传统Web应用程序
原文作者: Christian Findlay 原文链接: https://christianfindlay.com/2020/07/09/blazor-vs-traditional-web-apps ...
- #2020征文-开发板# 用鸿蒙开发AI应用(三)软件篇
目录: 前言 HarmonyOS 简介 DevEco Device Tool(windows下) 获取源码(切换到ubuntu) 烧录程序(切换回windows) 前言上一篇,我们在 Win10 上用 ...
- Linux下安装svn教程
前言 最近买了新服务器,准备开始弄一些个人的开源项目.有了服务器当然是搞一波svn啦.方便自己的资料上传和下载.于是在此记录搭建svn的方式,方便以后直接使用. 安装 使用yum源进行安装,十分的方便 ...
- gin框架的路由源码解析
前言 本文转载至 https://www.liwenzhou.com/posts/Go/read_gin_sourcecode/ 可以直接去原文看, 比我这里直观 我这里只是略微的修改 正文 gin的 ...
- Head First 设计模式 —— 11. 组合 (Composite) 模式
思考题 我们不仅仅要支持多个菜单,升值还要支持菜单中的菜单.你如何处理这个新的设计需求? P355 [提示]在我们的新设计中,真正需要以下三点: P354 我们需要某种属性结构,可以容纳菜单.子菜单和 ...
- Head First 设计模式 —— 13. 代理 (Proxy) 模式
思考题 如何设计一个支持远程方法调用的系统?你要怎样才能让开发人员不用写太多代码?让远程调用看起来像本地调用一样,毫无瑕疵? P435 已经接触过 RPC 了,所以就很容易知道具体流程:客户端调用目标 ...
- MySQL常用的一些(就几个)聚合函数
聚合函数 (常用) 函数名称 描述 CONUT() 记数 SUM() 求和 AVG() 平均值 MAX() 最大值 MIN() 最小值 -- ================= 聚合函数 ====== ...
- iconv函数报错 Detected an illegal character in input string
近日使用php代码导出文件为excel,一直乱码.导出修改编码都无效,最后发现,是需要修改php导出代码本身的编码.首先用记事本打开php代码,另存为,选择ANSI格式.然后打开iconv函数这个ph ...