Effective Java(第三版) 学习笔记 - 第四章 类和接口 Rule20~Rule25

目录

Rule20 接口优于抽象类

Rule21 为后代设计接口

Rule22 接口只用于定义类型

Rule23 类层次优于标签类

Rule24 静态成员类优先于非静态成员类

Rule25 限制源文件为单个顶级类

附赠一个问题:可以自定义一个java.lang.String吗?


Rule20 接口优于抽象类

  • 如果想要不改变当前的代码层次结构,又想拥有新的功能增加,显然考虑用mixin(混合类型)来实现最为合适。例如,Comparable就是一个mixin接口。接口允许构造非层次结构的类型框架。

public interface Comparable<T> {public int compareTo(T o);
}public class BigDecimal extends Number implements Comparable<BigDecimal> {...public int compareTo(BigDecimal val) {// Quick path for equal scale and non-inflated case.if (scale == val.scale) {long xs = intCompact;long ys = val.intCompact;if (xs != INFLATED && ys != INFLATED)return xs != ys ? ((xs > ys) ? 1 : -1) : 0;}int xsign = this.signum();int ysign = val.signum();if (xsign != ysign)return (xsign > ysign) ? 1 : -1;if (xsign == 0)return 0;int cmp = compareMagnitude(val);return (xsign > 0) ? cmp : -cmp;}
}public final class Boolean implements java.io.Serializable, Comparable<Boolean> {public int compareTo(Boolean b) {return compare(this.value, b.value);}public static int compare(boolean x, boolean y) {return (x == y) ? 0 : (x ? 1 : -1);}
}
  • 利用Rule18中的装饰器模式,接口使得安全地增强类的功能变得可能。
  • Java8中,接口允许拥有缺省实现,显而易见、相比于之前,接口的能提供的功能变得更加强大。

虽然可以考虑用缺省方法来提供一些默认实现,但是由于缺省方法拥有一些局限性,并不是什么情况下都可以用接口的缺省方法来实现。这个时候可以对接口提供一个抽象的骨架实现类,结合接口和抽象类的优点。接口负责定义类型、提供一些缺省方法,骨架实现类负责实现除基本类型接口方法之外,剩下的非基本类型接口方法。而这,就是设计模式中的模板模式。例如,我们大家常用的集合中:AbstractCollection<E>、AbstractList<E>、AbstractSet<E>、AbstractQueue<E>等都是骨架实现的样例。

需要注意的是,不允许为Object中的equals、hashCode、toString方法提供接口的缺省实现,所以这些方法都放在了骨架实现类中来完成。

Rule21 为后代设计接口

如果针对一个历史已有的现存接口,想要对它进行功能扩展,增加一些方法,如何实现?

  • Java8以前

想要扩展接口方法,那么必须把所有实现该接口的类全部实现一遍新的扩展方法,如果该接口的实现类都是公司内部使用,虽然是一件麻烦事,但是起码还能完成任务。但是如果这个接口是对外提供的,使用者更新了版本之后,发现项目中出现大量的编译报错,等待着完成新的扩展方法进行实装,估计开发人员头都大了。所以我们可能优先会考虑采用mixin混合类型等等方式来进行拓展功能。

  • Java8之后

相比之前要相对简单一点,可以考虑采用java8接口的新特性,缺省实现来完成扩展。但是需要注意的是,就算接口拥有缺省实现了,也是一件拥有风险的事情。

例如,java.util.Collection<E>中新增了removeIf,用于函数式编程。但是该书撰写直到出版时,貌似org.apache.commons.collections4.Collection.SynchronizedCollection中,这个类来自Apache Commons类库,但是并没有针对removeIf做同步锁的实装。这样一来,如果在那个版本使用了removeIf,由于java.util.Collection<E>中的removeIf并没有加锁控制删除,如果另一个线程也同时操作了该集合,就会导致ConcurrentModificationException。而这由于是缺省方法来拓展的,编译并不会报错,只有到了真正运行时才会发现。

缺省方法提供了一种解决方案用来进行接口的未来拓展,但是也不要觉得缺省方法就是万能的。

public interface Collection<E> extends Iterable<E> {...default boolean removeIf(Predicate<? super E> filter) {Objects.requireNonNull(filter);boolean removed = false;final Iterator<E> each = iterator();while (each.hasNext()) {if (filter.test(each.next())) {each.remove();removed = true;}}return removed;}...
}

Rule22 接口只用于定义类型

接口是为了什么而创建,接口只用于充当可以引用这个类的实例类型。除此之外,为了其他任何其他目的而定义接口都是不恰当的。

书中指出,有些开发人员会用接口,来定义常量接口,其目的就是为了使用常量数据。而这是不良的设计。

// 请不要这么做
// Constant interface antipattern - do not use!
public interface PhysicalConstants {// Avogadro's number (1/mol)static final double AVOGADROS_NUMBER   = 6.022_140_857e23;// Boltzmann constant (J/K)static final double BOLTZMANN_CONSTANT = 1.380_648_52e-23;// Mass of the electron (kg)static final double ELECTRON_MASS      = 9.109_383_56e-31;
}

我们一般定义常量数据,要么是用枚举,要么是不可实例化的常量类。

// 推荐的常量类实现
// Constant utility class (Page 108)
public class PhysicalConstants {private PhysicalConstants() { }  // Prevents instantiation// Avogadro's number (1/mol)// java7开始,数字支持下划线用来标明间隔,三位一组,表示千分位public static final double AVOGADROS_NUMBER = 6.022_140_857e23;// Boltzmann constant (J/K)public static final double BOLTZMANN_CONST  = 1.380_648_52e-23;// Mass of the electron (kg)public static final double ELECTRON_MASS    = 9.109_383_56e-31;
}

Rule23 类层次优于标签类

这个很容易理解,直接看代码就懂了。什么是标签类?

// Tagged class - vastly inferior to a class hierarchy! (Page 109)
class Figure {enum Shape { RECTANGLE, CIRCLE };// Tag field - the shape of this figurefinal Shape shape;// These fields are used only if shape is RECTANGLEdouble length;double width;// This field is used only if shape is CIRCLEdouble radius;// Constructor for circle// 圆Figure(double radius) {shape = Shape.CIRCLE;this.radius = radius;}// Constructor for rectangle// 矩形Figure(double length, double width) {shape = Shape.RECTANGLE;this.length = length;this.width = width;}double area() {switch(shape) {case RECTANGLE:return length * width;case CIRCLE:return Math.PI * (radius * radius);default:throw new AssertionError(shape);}}
}

标签类过于冗长、冗余出错,并且效率低下,如果新增三角形、正方向、平行四边形等等,用这种方式先不讲看着是否舒适,需要多少种构造函数用来定义并且区分形状呢。推荐用具有层次结构的方式实现,也就是继承。

// Class hierarchy replacement for a tagged class  (Page 110-11)
abstract class Figure {abstract double area();
}// Class hierarchy replacement for a tagged class  (Page 110-11)
// 圆
class Circle extends Figure {final double radius;Circle(double radius) { this.radius = radius; }@Override double area() { return Math.PI * (radius * radius); }
}// Class hierarchy replacement for a tagged class  (Page 110-11)
// 矩形
class Rectangle extends Figure {final double length;final double width;Rectangle(double length, double width) {this.length = length;this.width  = width;}@Override double area() { return length * width; }
}

想要扩展也变得较为方便。

// Class hierarchy replacement for a tagged class  (Page 110-11)
// 正方形
class Square extends Rectangle {Square(double side) {super(side, side);}
}

Rule24 静态成员类优先于非静态成员类

没什么好说的,类、属性、方法等一系列内容,能用静态声明肯定都是优先定义static。

如果不需要访问外部类实例,就优先考虑创建静态内部类。不然如果是非静态内部类,每个内部类实例都会包含一个额外的指向外部类实例对象的引用。

Java4种嵌套类详细介绍请看这篇 Java中四种嵌套类对比

Rule25 限制源文件为单个顶级类

乍一看这句话,感觉这条规则很深奥的样子,什么是源文件,什么是顶级类?

如果我换一句普通的话来描述,很简单:每个java文件中,只允许定义一个与文件名相同类名的类。

源文件:就是我们平常开发的java文件。

顶级类:就是没有像内部类一样进行嵌套定义。如果看过上条规则中那篇详细介绍,仔细留意就会发现有个编译报错信息是

The method XXX cannot be declared static; static methods can only be declared in a static or top level type

其中top level type就是顶级类的意思。

一般初学Java开发的同学,我们都会和他们说,类名必须和文件名相同,可能对于初学者也不会深思为什么要这么定义。往往一份java文件中也不会看见有第二个定级类的样例。但是其实这是可以定义出的。

Utensil.java

// Two classes defined in one file. Don't ever do this!  (Page 115)
class Utensil {static final String NAME = "pan";
}class Dessert {static final String NAME = "cake";
}

Main.java

public class Main {public static void main(String[] args) {System.out.println(Utensil.NAME + Dessert.NAME);}
}打印结果----
pancake

如果这时在相同的package下,我增加一份Dessert.java会怎么样?

Dessert.java

// Two classes defined in one file. Don't ever do this! (Page 115)
class Utensil {static final String NAME = "pot";
}class Dessert {static final String NAME = "pie";
}

编译器会告诉你编译报错:

所以我们要保持,一个java文件中只存在一个和文件名完全相同类名的类。

附赠一个问题:可以自定义一个java.lang.String吗?

附赠一个曾经被面试时,面试官问的问题:我们可以自定义一个java.lang.String吗?我特意询问了是包名、类名和java提供的String完全相同的么。他说是,包名类名完全一样,项目中也存在jdk中的rt.jar,然后问我行不行。

我当时回答了:肯定不能啊,你创建,会编译报错。

然后面试官笑着让我回去自己试试....

我真的回去后再次试了下,还是编译报错啊。百度了下,然后我发现,这个问题竟然还是网上的面试题中的一个,竟然还有博客介绍,那是面试者没有领会面试官真正的含义,其实面试官实际考察的是类加载机制-双亲委派机制。我只能表示我level比较低了,为啥不能直接问知不知道类加载机制。

嗯、按照网上的介绍,我真的用命令进行编译运行

用命令编译的确是通过了,也生成了String.class文件。但是问题是我们日常的开发工作谁是用文本编辑器写代码,用命令行进行编译。给大家看看用eclipse工具下实际的情况吧。

我只能说,我的确当时回答的片面了,但是日常的开发工具想要在java.lang下面自定义一些,哪怕是不存在于java.lang中的类都是不可以的,会让你换个package名。编译都还没通过,怎么就扯到了类加载机制,谁又能想到是让你用命令行来操作的呢。脱离实际日常的开发面试题,怎么说呢,个人觉得没有实际意义,不如直接问类加载了。

本文技术菜鸟个人学习使用,如有不正欢迎指出修正。xuweijsnj

Effective Java(第三版) 学习笔记 - 第四章 类和接口 Rule20~Rule25相关推荐

  1. Effective Java 第三版读书笔记(类和接口)

    第15条.使类和成员的可访问性最小化 对于成员有四种可访问级别: 1.私有的(private)----- 只有在声明该成员的顶层类内部才可以访问这个成员. 2.包级私有的(package-privat ...

  2. C程序设计-谭浩强 第三版-学习笔记第1章 C语言概述

    第一章 C语言概述 1.C语言历史背景 C语言是在B语言的基础上发展起来的,兼具一般高级语言和低级语言的优点,可用来编写系统软件或应用软件. 1972-1973年,贝尔实验室在B语言基础上设计出C语言 ...

  3. C程序设计-谭浩强 第三版-学习笔记 第2章 程序的灵魂 算法

    第 2 章 算法 --程序的灵魂 程序 = 算法 + 数据结构 (沃思,计算机科学家) 一个程序应该包括两方面: 对数据的描述:在程序中要指定数据的类型和数据的组织形式,即数据结构(data stru ...

  4. Effective Java第三版有哪些新功能?

    自从听说即将出版的有效Java 第三版以来,我一直想知道其中有什么新内容. 我假设将涵盖自Java 6以来引入Java的功能,的确如此. 但是,第三版Java开发人员经典版也有一些其他更改. 在本文中 ...

  5. 《Python编程:从入门到实战》(第2版)学习笔记 第5章 if语句

    [写在前面]为进一步提高自己的python代码能力,打算把几本经典书籍重新过一遍,形成系统的知识体系,同时适当记录一些学习笔记,我尽量及时更新!先从经典的<Python编程:从入门到实战> ...

  6. PMBOK(第六版) 学习笔记 ——《第一章 引论》

    系列文章目录 PMBOK(第六版) 学习笔记 --<第一章 引论> PMBOK(第六版) 学习笔记 --<第二章 项目运行环境> PMBOK(第六版) 学习笔记 --<第 ...

  7. 计算机网络(第7版)谢希仁著 学习笔记 第四章网络层

    计算机网络(第7版)谢希仁著 学习笔记 第四章网络层 第四章 网络层 4.3划分子网和构造超网 p134 4.3.1划分子网 4.3.2使用子网时分组的转发 4.3.3无分类编址CIDR(构建超网) ...

  8. 机器人导论(第四版)学习笔记——第四章

    机器人导论(第四版)学习笔记--第四章 4.1 引言 4.2 解的存在性 4.3 当n<6时操作臂子空间的描述 4.4 代数解法和几何解法 4.5 简化成多项式的代数解法 4.6 三轴相交的Pi ...

  9. 数据库系统概论(第5版)学习笔记第1章 1.2——数据模型

    数据库系统概论(第5版)学习笔记第1章 1.2--数据模型 目录 数据库系统概论(第5版)学习笔记第1章 1.2--数据模型 0.思维导图(自制) 1️⃣思维导图下载 2️⃣图示 1.2数据模型 1. ...

最新文章

  1. C++ 继承和派生 及 学生管理范例
  2. Eclipse里选择Servlet Run As Server后,自动生成了哪些资源?
  3. 【声传播】——多层介质平面波的反射问题
  4. 【js Date】时间字符串、时间戳转换成今天,明天,本月等文字日期
  5. 大数据之-Hadoop3.x_MapReduce_序列化案例Debug调试---大数据之hadoop3.x工作笔记0101
  6. java脚本语言 dim_写给新手windows脚本的入门
  7. 问题:Warning: Attempt to present UINavigationController whose view is not in the window hierarchy
  8. 谷歌浏览器 Chrome 最新版离线安装包下载地址
  9. 阿里官方 Redis 开发规范
  10. android 弹幕stg模板,STG游戏在弹幕设计上有何讲究?
  11. 红米NOTE3 双网通(2015617)刷机包 解账户锁
  12. 《所谓情商高就是会说话》
  13. 关于mysql安装时无法正常启动0xc000007b的问题
  14. 临床执业助理医师(综合练习)题库【4】
  15. R Markdown 如何使用外部css
  16. Unity技术手册 - 生命周期LifetimebyEmitterSpeed-周期内颜色ColorOverLifetime-速度颜色ColorBySpeed
  17. 国产数据库“向未来”
  18. 精心挑选10款基于 jQuery 的图片360度旋转插件
  19. 在python中布尔表达式的应用——判断语句
  20. sed 在行首或者行尾添加文本

热门文章

  1. android 音量调节框,「Best Volume Widget」手机桌面独立音量调节小工具 (Android)...
  2. c语言程序设计 a,C语言程序设计试题A
  3. Linux——文件的权限管理、acl列表、特殊权限位、练习2
  4. 有测试狗狗好坏的软件吗,想要养狗的朋友们请一定看完全文,测试一下自己适不适合养狗 ​...
  5. 日本准备推行AI婚配,年轻人会为“爱情算法”买单吗?
  6. Yii2 中添加全局函数
  7. 中国人工智能有多厉害,未来机器人都能做手术?老外表示不可思议
  8. 我的世界java版_我的世界Java版1.16.5
  9. Brave vs Google Chrome:哪个浏览器更适合你?
  10. 一文探究OR值和RR值区别