Effective Java(第三版) 学习笔记 - 第四章 类和接口 Rule20~Rule25
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相关推荐
- Effective Java 第三版读书笔记(类和接口)
第15条.使类和成员的可访问性最小化 对于成员有四种可访问级别: 1.私有的(private)----- 只有在声明该成员的顶层类内部才可以访问这个成员. 2.包级私有的(package-privat ...
- C程序设计-谭浩强 第三版-学习笔记第1章 C语言概述
第一章 C语言概述 1.C语言历史背景 C语言是在B语言的基础上发展起来的,兼具一般高级语言和低级语言的优点,可用来编写系统软件或应用软件. 1972-1973年,贝尔实验室在B语言基础上设计出C语言 ...
- C程序设计-谭浩强 第三版-学习笔记 第2章 程序的灵魂 算法
第 2 章 算法 --程序的灵魂 程序 = 算法 + 数据结构 (沃思,计算机科学家) 一个程序应该包括两方面: 对数据的描述:在程序中要指定数据的类型和数据的组织形式,即数据结构(data stru ...
- Effective Java第三版有哪些新功能?
自从听说即将出版的有效Java 第三版以来,我一直想知道其中有什么新内容. 我假设将涵盖自Java 6以来引入Java的功能,的确如此. 但是,第三版Java开发人员经典版也有一些其他更改. 在本文中 ...
- 《Python编程:从入门到实战》(第2版)学习笔记 第5章 if语句
[写在前面]为进一步提高自己的python代码能力,打算把几本经典书籍重新过一遍,形成系统的知识体系,同时适当记录一些学习笔记,我尽量及时更新!先从经典的<Python编程:从入门到实战> ...
- PMBOK(第六版) 学习笔记 ——《第一章 引论》
系列文章目录 PMBOK(第六版) 学习笔记 --<第一章 引论> PMBOK(第六版) 学习笔记 --<第二章 项目运行环境> PMBOK(第六版) 学习笔记 --<第 ...
- 计算机网络(第7版)谢希仁著 学习笔记 第四章网络层
计算机网络(第7版)谢希仁著 学习笔记 第四章网络层 第四章 网络层 4.3划分子网和构造超网 p134 4.3.1划分子网 4.3.2使用子网时分组的转发 4.3.3无分类编址CIDR(构建超网) ...
- 机器人导论(第四版)学习笔记——第四章
机器人导论(第四版)学习笔记--第四章 4.1 引言 4.2 解的存在性 4.3 当n<6时操作臂子空间的描述 4.4 代数解法和几何解法 4.5 简化成多项式的代数解法 4.6 三轴相交的Pi ...
- 数据库系统概论(第5版)学习笔记第1章 1.2——数据模型
数据库系统概论(第5版)学习笔记第1章 1.2--数据模型 目录 数据库系统概论(第5版)学习笔记第1章 1.2--数据模型 0.思维导图(自制) 1️⃣思维导图下载 2️⃣图示 1.2数据模型 1. ...
最新文章
- C++ 继承和派生 及 学生管理范例
- Eclipse里选择Servlet Run As Server后,自动生成了哪些资源?
- 【声传播】——多层介质平面波的反射问题
- 【js Date】时间字符串、时间戳转换成今天,明天,本月等文字日期
- 大数据之-Hadoop3.x_MapReduce_序列化案例Debug调试---大数据之hadoop3.x工作笔记0101
- java脚本语言 dim_写给新手windows脚本的入门
- 问题:Warning: Attempt to present UINavigationController whose view is not in the window hierarchy
- 谷歌浏览器 Chrome 最新版离线安装包下载地址
- 阿里官方 Redis 开发规范
- android 弹幕stg模板,STG游戏在弹幕设计上有何讲究?
- 红米NOTE3 双网通(2015617)刷机包 解账户锁
- 《所谓情商高就是会说话》
- 关于mysql安装时无法正常启动0xc000007b的问题
- 临床执业助理医师(综合练习)题库【4】
- R Markdown 如何使用外部css
- Unity技术手册 - 生命周期LifetimebyEmitterSpeed-周期内颜色ColorOverLifetime-速度颜色ColorBySpeed
- 国产数据库“向未来”
- 精心挑选10款基于 jQuery 的图片360度旋转插件
- 在python中布尔表达式的应用——判断语句
- sed 在行首或者行尾添加文本
热门文章
- android 音量调节框,「Best Volume Widget」手机桌面独立音量调节小工具 (Android)...
- c语言程序设计 a,C语言程序设计试题A
- Linux——文件的权限管理、acl列表、特殊权限位、练习2
- 有测试狗狗好坏的软件吗,想要养狗的朋友们请一定看完全文,测试一下自己适不适合养狗 ​...
- 日本准备推行AI婚配,年轻人会为“爱情算法”买单吗?
- Yii2 中添加全局函数
- 中国人工智能有多厉害,未来机器人都能做手术?老外表示不可思议
- 我的世界java版_我的世界Java版1.16.5
- Brave vs Google Chrome:哪个浏览器更适合你?
- 一文探究OR值和RR值区别