Effective Java 类和接口 (19-25 小节)

发表于 1年以前  | 总阅读数:377 次

大家好 ,effective java 进度+1

19. 要么设计继承并提供文档说明,要么禁止继承

这一节主要在说,设计父类时,文档方法要写全,写完整,特别是方法之间有相互调用的,不然子类只重写其中一个,可能会有 bug

这里举了 remove 方法的例子,它里面调用到了 iterator 方法,并且在方法注释上有详细的说明

正例

而我们上文提到的 addAll 方法中注释没有写好,没有提到会调用 add 方法,容易导致子类在重写过程中犯错(上文算出 6 的例子)

反例

protected?

接着,作者便说了这么一句话

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 接口,也不能在 clone 或者 readObject 方法中调用可以被重写的方法
  • 禁止子类化的条件,final 和 私有化构造器

小结

看完感觉……对父类多了一点点了解~

  1. 在父类的构造器,或者实现 Cloneable , Serializable 接口的父类,不能在其中调用可以被重写的方法
  2. 可以恰当地提供 protected 方法,供子类去实现,优化性能
  3. 通过 final 和 私有化构造器 可以禁止子类化
  4. 写好注释

20. 接口优于抽象类

这个就是 单继承,多实现 了。

特点

  • 实现新接口,可以很容易地对现有类进行改造
  • 接口是定义mixin的理想选择
  • 接口允许构造 非层次 类型框架 ,比如 书中的这个例子 ,既是歌手,又是作曲家
  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, AbstractMapAbstractQueue

好的骨架设计例子

将 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, AbstractMapAbstractQueue

21. 为后代设计接口

这里主要介绍了 Java8 的新特点,default 方法

以及它可能带来的风险

比如 Collection 接口中新增了 removeIf 方法

但是apache 工具包中的org.apache.commons.collections4.Collection.SynchronizedCollection 工具类还没有覆盖到。

4.3版本

这就可能导致一些并发问题,抛出异常。

所以在 4.4 版本中就得紧急修复了

4.4 版本

再比如,如果我之前刚好定义了个方法,和 新增的 default 方法一样,但是可访问性缩小,那么就会出现编译期的异常

小结

default 方法虽然可以很方便的在接口中添加实现,但是要注意它带来的风险。

22. 接口只用于定义类型

这个我不服 哈哈哈。在工作中,已经习惯了把这些常量抽出来,对这几个接口进行管理,挺方便的

但作者认为 常量接口是对接口的不正确使用 ,还举了 java.io.ObjectStreamConstants 充当反例

作者认为 导出常量 的好方法

  1. 工具类
  2. 枚举

额,我还是觉得用 常量接口 就行了

23. 类层次优于标签类

标签类例子

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);
        }
    }
}

可以看出它的特点是:有多种类型,还有一个字段表示它是什么类型。

这种写法有很多弊端:可读性差,浪费内存,扩展性差

这应该是面向对象的基本功叭,该拆分的就拆分出来。

过~

24. 静态成员类优于非静态成员类

这一节主题是嵌套类 ,主要是在说

  1. 何时使用哪种嵌套类
  2. 为什么使用嵌套类

嵌套类有四种,后三种是内部类

  1. 静态成员类
  2. 非静态成员类
  3. 匿名类
  4. 局部类

静态成员类

比如:ArrayList 中的 ArrayListSpliterator ,这个在 Stream 源码篇章出现过,调用 stream() 方法时,会先去创建这个 Spliterator。

特点:可以访问外部类的所有属性

非静态成员类

比如 SubList

非静态成员类和静态成员类的不同

  1. static 关键字
  2. 是否需要访问外部实例,如果不需要,就加这个 static
  3. 非静态成员的实例都隐含地与其外部实例相关联,创建时需要额外的空间开销

匿名类

这里引用上面 好的骨架设计例子 ,可以看到它在使用时,就被声明和实例化

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 局部类。

四种类的应用场景:

  1. 如果一个嵌套类需要在一个方法之外可⻅,或者代码太⻓,使用成员类
  2. 如果一个成员类的每个实例都需要保留其宿主实例的引用,则使用非静态类,否则,使用静态类
  3. 如果一个嵌套类在方法内部,而你只需要在一个地方创建实例,同时类也提前定义好了,那直接使用匿名类,否则用局部类

25. 限制源文件为单个级类

就是不要将多个 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, AbstractMapAbstractQueue

21 小节:了解到 default 可能带来的风险。

22 小节:还是喜欢用 常量接口 哈哈,枚举类和工具类也在用

23 小节:面向对象的基本功~

24 小节:对 静态内部类,非静态内部类,匿名类,局部类 这四种嵌套类多了些了解,在 stream 源码中有 N 多例子参考

25 小节:一个文件一个类,非要挤一起考虑 静态内部类。

终于又看完一章了,啊~

本文由哈喽比特于1年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/xcdP5o6Js1HnYbwr72pddA

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:7月以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:7月以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:7月以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:7月以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:7月以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:7月以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:7月以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:7月以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:7月以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:7月以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:7月以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:7月以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:7月以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:7月以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:7月以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:7月以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:7月以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:7月以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:7月以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:7月以前  |  398次阅读  |  详细内容 »
 相关文章
Java 中验证时间格式的 4 种方法 1年以前  |  3308次阅读
Java经典面试题答案解析(1-80题) 3年以前  |  2596次阅读
CentOS 配置java应用开机自动启动 3年以前  |  2432次阅读
IDEA依赖冲突分析神器—Maven Helper 4年以前  |  2421次阅读
SpringBoot 控制并发登录的人数教程 4年以前  |  2135次阅读
 目录