effective java(第二版)读书笔记

在这本书里讲解了在Java编程中78条极具实用价值的经验规则,虽然是jdk是1.5的,但是其中的每条经验规则都值得我们去学习

创建和销毁对象

考虑使用静态工厂方法代替构造器

优势:
  • 静态工厂方法有名称

  • 在使用静态工厂的时候,针对经常重复请求创建相同的对象可以使用预先创建好的对象,避免重复创建,进而极大的提升性能,比如Boolean.valueOf(boolean b);

  • 可以返回类型的任何子类型的对象

  • 在创建参数化类型实例的时候,使代码更加简洁

缺点:
  • 类如果没有公有的构造器,将不能被子类化

解决办法就是:一个类要么为继承而设计,并提供文档说明,要么就禁止集成

遇到多个构造器参数时考虑使用构建器

重叠构造器

​ 提供一个必要参数的构造器,然后第二个构造器有一个可选参数,然后而第二构造器有两个可选参数,以此类推,调用的时候就调用最少参数的构造方法去实例化;

public Article(String title) {
    this(title,"默认作者");
}

public Article(String title, String author) {
    this.title = title;
    this.author = author;
}

使用重叠构造器不是不可以,但是当参数太多的时候,就会失去控制,代码变得繁重。

javaBeans模式

​ 利用无参的构造方法new一个对象,然后set属性的值

public static void main(String[] args) {
        Author author = new Author();
        author.setName("陈明羽");
        author.setAddress("北京");
}
Builder模式

​ builder模式模拟了具有名称的可选参数,这种方式可以有多个可变参数,可以利用方法进行复制,代码更易于阅读和编写,而且十分灵活。

/**
 * @auther: chenmingyu
 * @date: 2019/1/20 16:30
 * @description:
 */
public class Address {

    private Integer firstCode;

    private String firstName;

    private Integer secondCode;

    private String secondName;

    public static class Builder{

        private Integer firstCode;

        private String firstName;

        private Integer secondCode;

        private String secondName;

        public Builder(Integer firstCode, Integer secondCode) {
            this.firstCode = firstCode;
            this.secondCode = secondCode;
        }

        public Builder firstName(String firstName){
            this.firstName =firstName;
            return this;
        }

        public Builder secondName(String secondName){
            this.secondName =secondName;
            return this;
        }

        public Address builder(){
            return new Address(this);
        }
    }

    public Address(Builder builder) {
        this.firstCode = builder.firstCode;
        this.firstName = builder.firstName;
        this.secondCode = builder.secondCode;
        this.secondName = builder.secondName;
    }

    @Override
    public String toString() {
        return "Address{" +
                "firstCode=" + firstCode +
                ", firstName='" + firstName + '\'' +
                ", secondCode=" + secondCode +
                ", secondName='" + secondName + '\'' +
                '}';
    }

    public static void main(String[] args) {
        Address address = new Address.Builder(1,2).firstName("北京").secondName("丰台").builder();
        System.out.println(address.toString());
    }
}

使用私有构造器或者枚举类型强化singleton属性

如何写单例:

使用枚举实现单例

通过私有构造器强化不可实例化的能力

这个就是如果你写的这类不想被实例化,就将构造方法private化

避免创建不必要的对象

能用工厂获取对象的或者重复使用的对象就不要自己新new一个对象

避免使用终结方法

对于所有对象都通用的方法

覆盖equals时请遵守通用约定

覆盖equals方法是遵守通用原则

  • 自反省:对于非null的引用值x,x.equals(x)必须返回true

    (对于任何非null的引用值x,x.equals(null)必须返回false);

  • 对称性:x.equals(y)为true则y.equals(x)为true;

  • 传递性:x.equals(y)为true,y.equals(z)为true则x.equals(z)为true;

  • 一致性:多次调用结果一致;

    重写equals方法的时候一定要按照上面四个特性检查一下

覆盖equals时总要覆盖hashCode

​ 重写equals的时候一定要重写hashCode,否则所有跟hash有关的操作都可能会出现错误。

始终要覆盖toString

提供好的toString可以使类使用起来更舒适,尤其当对象比较大的时候 ,调用toString时应该返回一个摘要信息,像system.out.println的实现都依赖于toString方法。所以一个好的toString方法应该是对当前类的描述,而不是所有属性结构。

谨慎的覆盖clone

克隆

考虑实现comparable接口

排序

类和接口

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

一个方法如过被声明成private或者protected,一旦被Override之后,如过要再想改的话,就要考虑一下,就像是牵一发而动全身,而一个方法被声明为private,当你要改的时候就好弄了,想怎么改就怎么改

所以就要尽可能的使每个类成员或方法的可访问性最小化,尽量不被外界访问

java的访问级别

  • private 自己用的
  • package-private
  • protected 想让子类用的
  • public 对外提供的

在公有类中使用访问方法而非共有域

这条啥意思那,就是访问类的变量的时候都使用提供的公共方法,像类的变量都用private修饰,然后提供get/set方法来访问变量

使可变性最小化

不可变类是指在类的实例不会改变,从实例被创建开始,实例所包含的所有的信息在其声明周期内不再改变。

为了使类变成不可变的类,须遵守的5条规则

  • 不提供任何会修改对象状态的方法
  • 保证类不被扩展
  • 所有的域使用final修饰
  • 所有的域都是私有的
  • 确保对于任何可变组件的互斥访问:如果类具有可变对象的域,则必须确保该类的客户端无法获得指向这些对象的引用,并且不要使用客户端提供的对象引用来初始化可变的域

优点:

复合优于继承

要么为继承而设计,并提供文档说明,要么就禁止集成

为继承而设计,并提供文档说明
  • 文档必须说明可覆盖性和方法的自用性

    • 在文档注释尾部介绍可覆盖方法内部工作情况和调用其他方法的描述信息,就是对存在自用性的可覆盖方法,应该用文档精确描述调用细节
    • 为继承专门设计的类的唯一的测试方法就是编写子类。经验表明,3个子类通常就足以测试一个可扩展的类。
    • 可覆盖性: 可以被重写的方法
    • 自用性: 父类里可覆盖的方法调用了别的可覆盖的方法
      • 将可覆盖方法的代码移到一个私有的一个辅助方法,然后可覆盖方法调用这个方法,别的方法也是去调用这个私有的方法
    • 构造器决不能调用可被覆盖的方法,有可能会出错
如何禁止继承
  • final 修饰

  • private 修饰构造函数

  • 提供静态工厂获取对象

接口优于抽象类

java只允许单继承,多实现

接口允许我们构造非层次结构的类型框架

接口一旦被公开,切被广泛使用,在想修改基本上是不可能的

通过对导出的每个重要接口都提供一个抽象的骨架实现类,把接口和抽象类的优点结合起来(骨架实现类)

  • 骨架实现类:如:AbstractSet

接口只用于定义类型

接口是一种规范,可以充当实现类的类型,不应该被用来导出常量

反例:ObjectStreamConstants

如果常量与接口或类密切相关,就应该定义在接口或类中

类层次优于标签类

标签类

  • 缺点:过于冗长,可读性,有很多不相关的代码,增加了内存的占用

  • 类层次

    • 为每种原始标签类都定义根类的具体子类
    • 类层次的好处:直观反映类型层次之间关系,访问的时候直接访问,而不是通过访问方法

用函数对象表示策略

函数对象:主要用途就是实现策略模式

优先考虑静态成员类

嵌套类

  • 嵌套类有四种:
    • 静态成员类
      • 当外部类需要使用内部类,而内部类无需外部类资源,并且内部类可以单独创建的时候
    • 非静态成员类
    • 匿名类
    • 局部类
  • 静态成员类与非静态成员类的不同:
    • 静态成员类不需要有指向外部类的引用。但非静态成员类需要持有对外部类的引用