Effective Java读书笔记(一):类、接口、对象

前言

“可能有人认为我不需要任何Java方面的书籍,但是我需要这本” - James Gosling

Java领域有几本被奉为圣经的书籍,其中(我认为)最著名的两本是《Thinking in Java》以及《Effective Java》。如果说Java编程思想教会我们Java这门语言,那么Effective Java则教会我们如何写出更好的Java语言程序。

这本书对于API开发程序员更为对口,但是对于普通的API用户同样有非常大的价值。此次读书笔记基本会摘抄书本原文,按照自己的顺序来组织书中的条目,而一些比较罕见的条目则会忽略。


类和接口

1. 使类和成员的可访问性最小化

尽可能地使每个类或者成员不被外界访问,使用与你正在编写的软件的对应功能相一致的、尽可能最小的访问级别。能private就别缺省,能缺省就别public,类能内部就别公开。
公有(public)类永远都不应该暴露可变的域,因为如果不改变API就无法改变它的数据表示法。如果需要访问域,可以通过提供访问方法,即getter/setter。有一种情况例外,即公有静态final(public static final)的域,这是一种常量形态。

2. 类层次的选择

复合(composition)优先于继承,只有当子类真正是父类的子类型,即子与父为”is-a”关系的时候,才应该使用继承,否则通常情况下,”子类”包含”父类”的一个实例,转发”父类”消息。
接口优先于抽象类,接口通常是定义允许多个实现类型的最佳选择,只有当演变的容易性比灵活性和功能更为重要的时候才应该使用抽象类。
类层次优先于标签类,”标签类”可以理解为冗长的switch-case/if-else语句,一般情况下可以使用Java的多态特性将其重构,也即策略模式。

3. 内部类

内部类有四种:静态成员类、非静态成员类、匿名类、局部类。

  • 静态成员类可视为普通的类,常见用法是作为辅助类,仅当与外部类一起使用时才有意义。
  • 非静态成员类的每个实例都隐含着与外围类的一个外围实例相关联,而这份引用可能会导致外围类在符合GC条件时由于被内部类引用着而无法GC。如果不需要外围实例,优先使用静态成员类。
  • 匿名类的常见用法是动态创建函数对象,实现策略。
  • 局部类与局部变量用法相仿,作用域规则也相同,一般也很少用…匿名类和局部类在非静态环境中定义时才有外围实例,并且不能包含静态成员。

构造器与对象

1. 多个构造器参数时使用Builder模式

Android开发者如果使用过Square的开源项目,对Builder模式必然不会陌生,通过链式调用最后build出一个目标对象。当大多数参数都是可选的时候使用Builder模式是比较优雅的方式,如果参数是必须时同样可以使用Builder模式,但需要做好说明工作。

2. 枚举实现Singleton属性

Singleton代表那些唯一的类对象。网上流传有多种单例的写法,有一篇著名的”单例的7种写法”~~,本书推荐的写法是使用静态成员,公有和私有都可以,私有则需要提供访问方法。

public class Singleton {
    public/private static final Singleton INSTANCE = new Singleton();
    private Singleton(){}
    ...
}

然而,如果涉及到序列化时,上面的方式需要额外的动作。比较好的实践方式是使用枚举。

public enum Singleton {
    INSTANCE;

    private int mFile;
    public void method();
}
3. 考虑用静态工厂代替构造器

这样做有几大优势:

  • 静态工厂有名称;
  • 可以做到不必每次调用它们的时候都创建一个新对象,比如上述的单例模式;
  • 可以返回子类对象;
  • 创建泛型类的实例可以使代码变得简单。

    public static <K,V> HashMap<K,V> newInstance() {
        return new HashMap<K,V>();
    }
4. 避免创建不必要的对象
  • 能只实例化一次某个对象,就尽量别多次实例化, 尤其在循环体中;
  • 注意自动装箱使基本类型装箱为对象,尤其在循环体中。
  • 池技术的使用,最经典的莫过于String,String str = "foo"String str = new String("foo"),前者使用了常量池,后者则额外实例化了一个对象。
5. 消除过期的对象引用

这个话题和内存泄露息息相关,而内存泄露又是另外一个很大的话题,所以此处不讨论内存泄露。总而言之,如果一个对象已经无用,应该清空它的引用,即将这个对象的引用置为null。当程序员第一次被类似这样的问题困扰时,往往会过分小心:对于每一个对象引用,一旦不需要时就置为null,然而清空对象引用应该是一种例外而不是规范行为,消除过期引用最好的方法是让包含该引用的变量结束其生命周期。

6. 避免使用终结方法

需要清理资源时使用try-finally结构,在finally调用显式的清理方法,比如输入输出流的close方法等。终结方法是不可预测的,也是很危险的,应该避免使用终结方法。

7. 覆盖equals和hashCode方法

Object类这两个方法的覆盖很有讲究,但是IDE已经帮我们解决了。我们要知道的是:当类具有逻辑相等的概念时,即比较的是内容而非是否指向同一对象,就需要覆盖equals方法;在每个覆盖了equals方法的类中一般也需要覆盖hashCode方法,否则基于散列的集合可能出错。这两个方法更多讨论可以看之前一篇博文Java:equals和hashCode的理解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值