第一章: 创建和销毁对象

  • 类可以通过静态工厂方法来提供客户端,而不是通过构造器

优点:

  • 自定义工厂名称,提高可读性

  • 可以工厂里搞单例

  • 控制实例类是哪种子类

总之是更加灵活,可读性更高

缺点:

  • 有可能会导致类无法子类化,因为一般搞工厂,就把构造器私有或受保护了

  • 有心的使用者会困惑,总是想看看到底是咋实例化的,单例? 多例? 创建时是否有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要点笔记的更多相关文章

  1. 《Effective Java》笔记45-56:通用程序设计

    将局部变量的作用域最小化,可以增强代码的可读性和可维护性,并降低出错的可能性. 要使用局部变量的作用域最小化,最有力的方法就是在第一次使用它的地方才声明,不要过早的声明. 局部变量的作用域从它被声明的 ...

  2. Effective java读书笔记

    2015年进步很小,看的书也不是很多,感觉自己都要废了,2016是沉淀的一年,在这一年中要不断学习.看书,努力提升自己 计在16年要看12本书,主要涉及java基础.Spring研究.java并发.J ...

  3. Effective Java读书笔记完结啦

    Effective Java是一本经典的书, 很实用的Java进阶读物, 提供了各个方面的best practices. 最近终于做完了Effective Java的读书笔记, 发布出来与大家共享. ...

  4. Effective Java阅读笔记——引言

    “我很希望10年前就拥有这本书.可能有人认为我不需要任何Java方面的书籍,但是我需要这本书.” ——Java之父 James Gosling 在图书馆找到这本java著作时,首先看到了这句话.   ...

  5. Effective Java 学习笔记之第七条——避免使用终结(finalizer)方法

    避免使用终结方法(finalizer) 终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的. 不要把finalizer当成C++中析构函数的对应物.java中,当对象不 ...

  6. Effective Java 读书笔记(一):使用静态工厂方法代替构造器

    这是Effective Java第2章提出的第一条建议: 考虑用静态工厂方法代替构造器 此处的静态工厂方法并不是设计模式,主要指static修饰的静态方法,关于static的说明可以参考之前的博文&l ...

  7. Effective Java 读书笔记之九 并发

    一.访问共享的可变数据时要同步 1.synchronized关键字既然保证访问的可见性也能保证原子性.而volatile修饰符只能保证变量的线程可见性. 2.增量操作符等不是原子性,多线程操作时可能导 ...

  8. Effective Java 读书笔记之七 通用程序设计

    一.将局部变量的作用域最小化 1.在第一次使用变量的地方声明 2.几乎每个变量的声明都应该包含一个初始化表达式:try-catch语句是一个例外 3.使方法小而集中是一个好的策略 二.for-each ...

  9. Effective Java 读书笔记之一 创建和销毁对象

    一.考虑用静态工厂方法代替构造器 这里的静态工厂方法是指类中使用public static 修饰的方法,和设计模式的工厂方法模式没有任何关系.相对于使用共有的构造器来创建对象,静态工厂方法有几大优势: ...

随机推荐

  1. JS 增删改查操作XML

    效果图: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <titl ...

  2. 前端每日实战:17# 视频演示如何用纯 CSS 创作炫酷的同心矩形旋转动画

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/bMvbRp 可交互视频教程 此视频 ...

  3. 从0开始搭建一个阿里云java部署环境

    一.购买服务器 https://www.aliyun.com/daily-act/ecs/activity_selection?spm=5176.8112568.738194.8.674c9ed53Y ...

  4. spring boot Shiro JWT整合

    一个api要支持H5, PC和APP三个前端,如果使用session的话对app不是很友好,而且session有跨域攻击的问题,所以选择了JWT 1.导入依赖包 <dependency> ...

  5. 结题报告--P5551洛谷--Chino的树学

    题目:点此 题目描述 Chino树是一棵具有某种性质的满二叉树,具体来说,对于这棵树的每一个非叶子节点,它的左子节点(A)(A)(A)的右子节点(C)(C)(C)与它的右子节点(B)(B)(B)的左子 ...

  6. Spark RDD Tutorial

    Spark RDD教程 这个教程将会帮助你理解和使用Apache Spark RDD.所有的在这个教程中使用的RDD例子将会提供在github上,供大家快速的浏览. 什么是RDD(Rssilient ...

  7. flask 密码加密 视频资料

    https://www.bilibili.com/video/av53870541/?p=33 通过在数据模型中定义函数来实现加密 from werkzeug.security import gene ...

  8. 1构建个人博客--使用Hugo快速成型

    概述 人在武汉,病毒肆虐. 隔离久了,有点闷,闲余时间找点事情做. 建个博客吧, 内容不重要,写不写也不那么要紧,目前水平也写不出什么有深度的东西. 但是这个姿势一定要优美, 过程一定要折腾. OK, ...

  9. 处理asp.net core连接mysql的一个异常Sequence contains more than one matching element

    晚上在那里调程序,把mysql.data组件的nuget包进行了更新,前几天好好的程序,开始抛错,跟踪断点发现以下的异常: Unable to connect to any of the specif ...

  10. Oracle根据实体类比对2个数据库结构差异(demo)

    源起 在公司做项目时 经常出现 实体结构和线上的数据结构以及公司开发库数据结构不匹配的问题 但是又不能直接把开发库导入到生产库因为生产库已经有实际数据了 所以弄了一个小工具 此处只做记录用 demo级 ...