Effective Java要点笔记
第一章: 创建和销毁对象
- 类可以通过静态工厂方法来提供客户端,而不是通过构造器
优点:
自定义工厂名称,提高可读性
可以工厂里搞单例
控制实例类是哪种子类
总之是更加灵活,可读性更高
缺点:
有可能会导致类无法子类化,因为一般搞工厂,就把构造器私有或受保护了
有心的使用者会困惑,总是想看看到底是咋实例化的,单例? 多例? 创建时是否有init一些前置过程?
getInstance 约定俗成返回单例
newInstance 约定俗成多例
getType 一般把工厂方法写在其它类(如专门的工厂类) 可根据Type入参来从工厂拿对应单例
newType 一般把工厂方法写在其它类(如专门的工厂类) 可根据Type入参来从工厂拿对应多例
多个构造参数,如果灵活多变,要考虑用构造器
工作中如果构造函数有多个 且 特定 的话,我一般倾向写两三个函数签名不一样构造器。但是如果在构造参数很多且多变,要写一个内部构建器,用builder模式,而不是大量重叠构造器。
优点:
builder 可以一次构建实例对象,而JavaBean的方式虽然比构造器可读性好点,但会使对象状态处于不一致的状态,线程安全维护成本太高了。因为总是要setter方法赋值
builder方式创建的实例是不可变的,无状态的。
builder方式在进行构造时可以加入校验参数的逻辑确保正确的通过builder构建实例
builder 可以在真正创建对象之前进行各种参数修改调整,甚至可以自动设置某些域
builder 因为是变化的,从抽离变化的角度来看,可以将builder设计成接口
public interface Builder<T> { public T build(); }
缺点:
静态内部类builder明显代码量增加了
创建实例还得搞个builder 额外的性能开销
总结: 个人感觉构造参数稳定的情况下,即未来不会参数变化频繁 && 参数比较少,还是使用重叠构造器的方式,感觉这也在好多源码中约定俗成的。如果构造类时需要多个参数,特别是当大多数参数都是可选的时候,Builder 不失为一个很好的选择。可读性和安全性都能保障。
再讲单例实现
常见的有三种 枚举 静态属性或静态块 双重检查锁不需要实例话的类
尽量把构造器私有化,比如一些工具类,避免不必要的对象意外创建对象如果可重用,就少创建点
但是如果因为多创建了实例而提高了程序的清晰性,间接性和功能性,也是一个好事儿消除某些过期的对象引用, 因为可能导致内存泄漏
一种情形是 数组, 还有 缓存, 可以用WeakHashMap解决,但是必须保证所有的缓存项的生命周期是由该键的外部引用而不是由值决定时,WeakHashMap才有意义, LinkedHashMap 可以自定义缓存策略,LRU常用实现。监听器和回调的内存泄漏风险。关于java的 finalize方法
其实工作中极少用到它,一般都是显示的public关闭资源的方法,让客户端去显示关闭,服务端也可以配合try catch finally 写个确保释放资源的操作(万一客户端脑残不调close方法),子类覆盖finalize方法注意super调用父类的finalize方法。可以搞个private final 内部类 里面有个回收外部类实例资源的方法,外部类私有属性保持对内部类实例的一个引用。内部 外部类现在同生共死了,当外部类死掉的时候,内部类实例也要死,死的时候把外部类资源回收了。关于覆盖equals的相关事项
类的每个实例都只与他自身相等
类是私有的或者是包级私有的,那么可以确定它的equals方法永远不会被调用,这时候需要覆盖equals方法,防止被意外调用
如果要判断“逻辑相等“,且父类equals做不到这个功能的时候需要覆写equals
枚举值类,因为是“每个值至多只存在一个对象“ 的类, 搜衣不需要覆写
equals含义的通用约定一定要遵守!!!没有哪个类是孤立的。
equels方法诀窍:
== 判断是否是同一个对象的引用
instanceof 进行类型检查
把参数转换为正确的类型
检查参数的每个域是否一一对应的equals
覆盖equals必须覆盖hashCode,相等的对象必须具有相同的hashCode值~
不要将equals声明中的Object对象替换为其它的类型,应该覆盖Object的 equals方法
要始终覆盖toString方法,打印的信息更加具有可读性
第四章: 类与接口
要区别设计良好的模块与设计不好的模块,最重要的因素在于,这个模块对于外部的其它模块而言,是否隐藏其内部数据和其它实现细节。
尽可能地使每个类或者成员不被外界访问
对于包内顶层地类和接口,要么包级私有要么public, 一旦public开发者有责任永远支持它
如果包级私有地顶层类只被包内的一个类用到,要考虑使它成为那个类的私有嵌套类,使可访问范围更小
实例域和静态域绝不能是公有的
对于final数组域可以这样控制权限
private static final Thing[] PRIVATE_VALUES = {...};
public static final List<Thing> VALUES = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
用函数表示策略, 策略抽象成接口,如果实现接口的具体策略只使用一次,用匿名类,否则应该定义一个静态final函数, 返回类型为策略接口
public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getValue().compareTo(c2.getValue());
}
静态成员类是外围类的一个成员, 常见用法是作为共有类的辅助类, 比如一个类里面有个静态枚举类;
非静态成员类的每个实例都隐含着与外部类一个外部实例相关联(影响外部类的垃圾回收)。当非静态成员类的实例被创建的是时候,它和外围实例之间的关联关系也随之建立起来&不能被修改
第七章: 关于方法
每当编写方法和构造器的时候,应该考虑他它的参数有哪些限制,应该把限制写到文档中,并在方法的开头出加上限制逻辑,私有方法assert断言
我们要保护性的去设计程序,如果API设计的不好,客户端很容易误解,并导致不可预期的行为,所以编写面对客户的不良行为时仍能保持健壮的类,这是非常值得投入时间去做的事情。要注意是否允许调用者修改其内部的组件,
关于方法签名的设计:
方法名称尽量要风格一致,并选择大众认可的名称
类的方法设计太多,会使类难以学习,使用,文档化,测试以及维护
避免过长的参数列表,目标参数个数4个以内,太长不好记,容易乱序
拆分参数子集为多个方法入参
将多个频繁出现的参数序列封装成静态成员类,并考虑使用builder方法构建
对于参数类型,要优先使用接口而不是类
对于boolean参数,要优先使用两个元素的枚举类型,例如在一个静态工厂中newInstance(PayType.WX)
易于阅读和编写
易于扩展
枚举常量内易于增加方法
对于多个具有相同参数数目的方法来说,应该尽量避免重载方法,重载是编译期确定调用哪个重载方法,覆写是在运行时
返回类型为数组或集合的方法应该返回一个零长度的数组或者集合
为了正确地编写API文档,必须在每个被导出的方法,类,接口,构造器和字段声明之前增加文档注释
方法的文档注释应该描述它与客户端的约定,而不是说这个方法是怎么干到的
前置条件 后置条件? 副作用 以及方法的线程安全性
@param @return @throw (if xxx then yyy) @code <pre> @code </pre>
文档注释的第一句话,是该注释所属元素的概要描述
要使局部变量的作用域最小化,最佳实践是在第一次使用它的地方声明它
异常
只针对异常的情况才使用异常, 不能利用异常来做其它投机取巧的逻辑
对于可恢复的情况且允许调用者能够进行适当的恢复使用受检异常, 其它异常使用运行时异常
优先使用jdk里的标准的异常,对于这些常见的可重用的异常会降低API的学习成本
.更高层的实现应该捕获低层的异常, 同时抛出可以按照高层抽象进行解释的异常,叫做异常转译, 这样避免了方法抛出的异常与它所执行的任务没有明显的联系, 让人不知所措
try {
} catch(LowerLevelException e) {
throw new HigherLevelException(...);
}
底层的异常被传到高层的异常, 高层的异常提供访问方法(Throwable.getCause)来获取底层的异常
不过我们应该在底层方法调用的时候尽量确保它们会执行成功,从而避免它们抛出异常,比如通过严格的检查高层传递到底层的参数。次选方案是,让高层悄悄的绕开异常, 将高层方法的调用者与底层问题隔离起来。(底层catch异常打错误日志)
一般而言,失败的方法调用应该使对象保持在被调用之前的状态
异常要打印关键信息,禁止忽略异常
还没关注我的公众号?
- 扫文末二维码关注公众号【小强的进阶之路】可领取如下:
- 学习资料: 1T视频教程:涵盖Javaweb前后端教学视频、机器学习/人工智能教学视频、Linux系统教程视频、雅思考试视频教程;
- 100多本书:包含C/C++、Java、Python三门编程语言的经典必看图书、LeetCode题解大全;
- 软件工具:几乎包括你在编程道路上的可能会用到的大部分软件;
- 项目源码:20个JavaWeb项目源码。
Effective Java要点笔记的更多相关文章
- 《Effective Java》笔记45-56:通用程序设计
将局部变量的作用域最小化,可以增强代码的可读性和可维护性,并降低出错的可能性. 要使用局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方才声明,不要过早的声明. 局部变量的作用域从它被声明的 ...
- Effective java读书笔记
2015年进步很小,看的书也不是很多,感觉自己都要废了,2016是沉淀的一年,在这一年中要不断学习.看书,努力提升自己 计在16年要看12本书,主要涉及java基础.Spring研究.java并发.J ...
- Effective Java读书笔记完结啦
Effective Java是一本经典的书, 很实用的Java进阶读物, 提供了各个方面的best practices. 最近终于做完了Effective Java的读书笔记, 发布出来与大家共享. ...
- Effective Java阅读笔记——引言
“我很希望10年前就拥有这本书.可能有人认为我不需要任何Java方面的书籍,但是我需要这本书.” ——Java之父 James Gosling 在图书馆找到这本java著作时,首先看到了这句话. ...
- Effective Java 学习笔记之第七条——避免使用终结(finalizer)方法
避免使用终结方法(finalizer) 终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的. 不要把finalizer当成C++中析构函数的对应物.java中,当对象不 ...
- Effective Java 读书笔记(一):使用静态工厂方法代替构造器
这是Effective Java第2章提出的第一条建议: 考虑用静态工厂方法代替构造器 此处的静态工厂方法并不是设计模式,主要指static修饰的静态方法,关于static的说明可以参考之前的博文&l ...
- Effective Java 读书笔记之九 并发
一.访问共享的可变数据时要同步 1.synchronized关键字既然保证访问的可见性也能保证原子性.而volatile修饰符只能保证变量的线程可见性. 2.增量操作符等不是原子性,多线程操作时可能导 ...
- Effective Java 读书笔记之七 通用程序设计
一.将局部变量的作用域最小化 1.在第一次使用变量的地方声明 2.几乎每个变量的声明都应该包含一个初始化表达式:try-catch语句是一个例外 3.使方法小而集中是一个好的策略 二.for-each ...
- Effective Java 读书笔记之一 创建和销毁对象
一.考虑用静态工厂方法代替构造器 这里的静态工厂方法是指类中使用public static 修饰的方法,和设计模式的工厂方法模式没有任何关系.相对于使用共有的构造器来创建对象,静态工厂方法有几大优势: ...
随机推荐
- JUC-八锁现象和不安全锁
1,被 synchronized 修饰的方法,锁的对象是方法的调用者(实例对象) 2,被 static 修饰的方法,锁的对象就是 Class模板对象,这个则全局唯一 问题7: 一个普通同步方法,一个静 ...
- Xcode调试之exc_bad_access以及 message sent to deallocated instance
如果出现exc_bad_access错误,基本上是由于内存泄漏,错误释放,对一个已经释放的对象进行release操作.但是xcode有时候不会告诉你错误在什么地方(Visual Studio这点做得很 ...
- 基于VR三维全景的虚拟展馆展览实现
VR三维全景虚拟现实技术的应用,能够通过全方位互动式来还原真实场景,令人产生一种身临其境的感觉,由于三维全景虚拟现实技术具有一定应用优势,其在企业与院校展示.建筑规划展示.酒店宾馆展示等方面都逐步得到 ...
- GCC 特性整理
1, attrib 属性 1.1 对齐指令 2,结构体名称 3,switch case 必需{} 否则会报错 a label can only be part of a statement and a ...
- 关于“关键字synchronized不能被继承”的一点理解
网上看到很多对关键字synchronized继承性的描述只有一句"关键字synchronized不能被继承",并没有描述具体场景,于是自己做了以下测试. //父类 public c ...
- ggplot2练习
图源于电力电子课本65页——电容滤波的单相不可控整流电路. f<-function(w,d) { l<-w/sqrt(w^2+1)*exp(-atan(w)/w)*exp(-d/w) r& ...
- JavaScript每日学习日记(0)
8.10.2019 1.JavaScript能改变HTML内容.属性.样式,能隐藏或显示HTML元素. 2.JavaScript函数可以任意数量被放置在<body>.<head> ...
- 数据结构和算法:Python实现二分查找(Binary_search)
在一个列表当中我们可以进行线性查找也可以进行二分查找,即通过不同的方法找到我们想要的数字,线性查找即按照数字从列表里一个一个从左向右查找,找到之后程序停下.而二分查找的效率往往会比线性查找更高. 一. ...
- Natas30 Writeup(sql注入)
Natas30: 本关是一个登录页面,查看源码,可以发现关键代码. if ('POST' eq request_method && param('username') &&am ...
- Python3学习之路~10.3 论事件驱动与异步IO
论事件驱动----详见:https://www.cnblogs.com/alex3714/articles/5248247.html Select\Poll\Epoll异步IO----详见:http: ...