大家好 ,effective java 进度+1
这一节主要在说,设计父类时,文档方法要写全,写完整,特别是方法之间有相互调用的,不然子类只重写其中一个,可能会有 bug
这里举了 remove 方法的例子,它里面调用到了 iterator 方法,并且在方法注释上有详细的说明
正例
而我们上文提到的 addAll 方法中注释没有写好,没有提到会调用 add 方法,容易导致子类在重写过程中犯错(上文算出 6 的例子)
反例
接着,作者便说了这么一句话
a class may have to provide hooks into its internal workings in the form of judiciously chosen protected methods
类必须以某种形式提供 hook,以便能够进入它的内部工作流程,比如 protected 方法
我思考了很久,结合作者提到的 removeRange 方法例子,大致应该是说,父类会有 protected 方法,但是方法效率可能不好,需要子类结合自身去优化,得到更好的方法。
注意 protected 只有子类才可以调用,如果我们的客户端不是子类是无法调用到这个方法的。
这里提到这个方法的时间复杂度达到 O(n^2),思索了很久后,发现这个主要是针对 ArrayList 这种 动态数组 形式的,这里 remove 时,需要移动后面的 n 个元素,时间复杂度 O(n), 会导致效率很差。
在这里,还发现 removeRange 方法在 jdk8 和 11 中是不同的,为啥升级了呢,请看看这个例子
public class SuperClass<E> extends ArrayList<E>{
public static void main(String[] args) {
SuperClass<String> list = new SuperClass<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
list.add("f");
System.out.println("before remove : " + list);
list.removeRange(3, 1);
System.out.println("after remove (3, 1) : " + list); //[a, b, c, b, c, d, e, f]
}
}
这里调用居然没报错的~ (jdk8)
// jdk8
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// clear to let GC do its work
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
从源码可以发现,这里并没有限制这个 fromIndex 必须小于 toIndex 。
难怪 removeRange 调用后数据还多了
// jdk11
protected void removeRange(int fromIndex, int toIndex) {
if (fromIndex > toIndex) {
throw new IndexOutOfBoundsException(
outOfBoundsMsg(fromIndex, toIndex));
}
modCount++;
shiftTailOverGap(elementData, fromIndex, toIndex);
}
/** Erases the gap from lo to hi, by sliding down following elements. */
private void shiftTailOverGap(Object[] es, int lo, int hi) {
System.arraycopy(es, hi, es, lo, size - hi);
for (int to = size, i = (size -= hi - lo); i < to; i++)
es[i] = null;
}
用 jdk11 的版本会抛出 IndexOutOfBoundsException 异常。
看完感觉……对父类多了一点点了解~
- 在父类的构造器,或者实现 Cloneable , Serializable 接口的父类,不能在其中调用可以被重写的方法
- 可以恰当地提供 protected 方法,供子类去实现,优化性能
- 通过 final 和 私有化构造器 可以禁止子类化
- 写好注释
这个就是 单继承,多实现 了。
public interface Singer {
AudioClip sing(Song s);
}
public interface Songwriter {
Song compose(int chartPosition);
}
public interface SingerSongwriter extends Singer, Songwriter{
AudioClip strum();
void actSensitive();
}
骨架实现类 (skeletal implementation) 被称为 AbstractInterface , 从名字就可以看出,是 抽象+接口 的组合。
比如 AbstractCollection, AbstractSet, AbstractList, AbstractMap ,AbstractQueue
将 int[] 转成 Integer List。这里利用匿名内部类 AbstractList,很轻易的实现了这个功能
static List<Integer> intArrayAsList(int[] a) {
Objects.requireNonNull(a);
// The diamond operator is only legal here in Java 9 and later
// If you're using an earlier release, specify <Integer>
return new AbstractList<Integer>() {
@Override public Integer get(int i) {
return a[i]; // Autoboxing (Item 6)
}
@Override public Integer set(int i, Integer val) {
int oldVal = a[i];
a[i] = val; // Auto-unboxing
return oldVal; // Autoboxing
}
@Override public int size() {
return a.length;
}
};
}
这里举了 AbstractMap.SimpleEntry 这个例子,它的最大特点就是 :不是抽象的 ,简单高效。
接口最大的优点便是灵活了,也不用过多介绍了
这里最大的收获便是知道了 骨架实现类 这个概念,以及对集合类的层次设计多了一些认识。
骨架实现类 (skeletal implementation) ,又称 AbstractInterface ,例子:AbstractCollection, AbstractSet, AbstractList, AbstractMap ,AbstractQueue
这里主要介绍了 Java8 的新特点,default 方法 。
以及它可能带来的风险
比如 Collection 接口中新增了 removeIf 方法
但是apache 工具包中的org.apache.commons.collections4.Collection.SynchronizedCollection
工具类还没有覆盖到。
4.3版本
这就可能导致一些并发问题,抛出异常。
所以在 4.4 版本中就得紧急修复了
4.4 版本
再比如,如果我之前刚好定义了个方法,和 新增的 default 方法一样,但是可访问性缩小,那么就会出现编译期的异常
default 方法虽然可以很方便的在接口中添加实现,但是要注意它带来的风险。
这个我不服 哈哈哈。在工作中,已经习惯了把这些常量抽出来,对这几个接口进行管理,挺方便的
但作者认为 常量接口是对接口的不正确使用 ,还举了 java.io.ObjectStreamConstants
充当反例
作者认为 导出常量 的好方法
额,我还是觉得用 常量接口 就行了
标签类例子
package effectivejava.chapter4.item23.taggedclass;
// Tagged class - vastly inferior to a class hierarchy! (Page 109)
class Figure {
enum Shape { RECTANGLE, CIRCLE };
// Tag field - the shape of this figure
final Shape shape;
// These fields are used only if shape is RECTANGLE
double length;
double width;
// This field is used only if shape is CIRCLE
double 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);
}
}
}
可以看出它的特点是:有多种类型,还有一个字段表示它是什么类型。
这种写法有很多弊端:可读性差,浪费内存,扩展性差
这应该是面向对象的基本功叭,该拆分的就拆分出来。
过~
这一节主题是嵌套类 ,主要是在说
嵌套类有四种,后三种是内部类
比如:ArrayList 中的 ArrayListSpliterator ,这个在 Stream 源码篇章出现过,调用 stream() 方法时,会先去创建这个 Spliterator。
特点:可以访问外部类的所有属性
比如 SubList
非静态成员类和静态成员类的不同
这里引用上面 好的骨架设计例子 ,可以看到它在使用时,就被声明和实例化 。
static List<Integer> intArrayAsList(int[] a) {
Objects.requireNonNull(a);
// The diamond operator is only legal here in Java 9 and later
// If you're using an earlier release, specify <Integer>
return new AbstractList<Integer>() {
@Override public Integer get(int i) {
return a[i]; // Autoboxing (Item 6)
}
@Override public Integer set(int i, Integer val) {
int oldVal = a[i];
a[i] = val; // Auto-unboxing
return oldVal; // Autoboxing
}
@Override public int size() {
return a.length;
}
};
}
这里也在 stream 源码篇章中有提到,makeRef 方法内部,有一个 ReducingSink 局部类。
就是不要将多个 class 放在一起,就像这样 ,如果其他文件也有类似的类,会导致编译出错。
package effectivejava.chapter4.item25;
// 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";
//}
如果一定要放一起,要改成 静态成员类
19 小节:了解到 父类 的使用过程中,一定要注意 方法重写 带来的风险,不能在它的 构造器,或者 Clone,序列化 之类的方法中添加方法。以及注释的重要性,protected 方法的 hook 属性
20 小节:便是 骨架实现类 这个概念了 (skeletal implementation) ,又称 AbstractInterface ,更好地了解了集合类的架构,例子:AbstractCollection, AbstractSet, AbstractList, AbstractMap ,AbstractQueue
21 小节:了解到 default 可能带来的风险。
22 小节:还是喜欢用 常量接口 哈哈,枚举类和工具类也在用
23 小节:面向对象的基本功~
24 小节:对 静态内部类,非静态内部类,匿名类,局部类 这四种嵌套类多了些了解,在 stream 源码中有 N 多例子参考
25 小节:一个文件一个类,非要挤一起考虑 静态内部类。
终于又看完一章了,啊~
本文由哈喽比特于1年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/xcdP5o6Js1HnYbwr72pddA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。