Effective Java笔记

使用Build模式

  • 不是直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器,得到一个builder对象
  • 然后客户端在builder对象上调用类似于setter的方法,来设置每个相关的可选参数
  • 最后,客户端调用无参的build方法来生成不可变的对象
//   在这当中 FF是不可变的,所有的默认参数都单独放到一个地方
public class FF {
    private final int a;
    private final int b;
    private final int c;

    // Build 模式模拟了含有具体名字的可选参数
    public static class Builder{
        private final int a;
        private final int b;
        private int c;

        public Builder(int a, int b) {     //两个参数 
            this.a = a;
            this.b = b;
            System.out.println(a);
            System.out.println(b);
        }

        public Builder c(int val){   //单独一个参数
            c = val;
            System.out.println(c);
            return this;
        }

        public FF build(){
            return new FF(this);
        }
    }
    private FF(Builder builder){
        a = builder.a;
        b = builder.b;
        c = builder.c;
    }
    public static void main(String []args){
        FF f = new Builder(1,2).c(100).build();
    }

}

  • build模式十分灵活,可以利用耽搁builder构建多个对象
  • builder的参数可以在创建对象期间进行调整,也可随着不同的对象而改变

优先使用基本类型而不是装箱基本类型

测试

public static void main(String []args){
        long startTime = System.currentTimeMillis(); //获取开始时间

        long sum = 0L;
        for(long i = 0; i < Integer.MAX_VALUE; i++){
            sum += i;
        }
        System.out.println(sum);
        long endTime = System.currentTimeMillis(); //获取结束时间
        System.out.println("程序运行时间:" + (endTime - startTime) + "ms"); //输出程序运行时间
    }

  • 只因为打错一个字符,变量sum被声明成Long而不是long,运行时间9254ms
  • sum改为long类型,运行时间为964ms

重写equals必须重写hashcode

反例

public class PhoneNum {
    private final short code;
    private final short prefix;


    public PhoneNum(int code, int prefix) {

        this.code = (short) code;
        this.prefix = (short) prefix;
        rangeCheck(code, 999, "code");
        rangeCheck(prefix, 9999, "prefix");
    }

    private static void rangeCheck(int arg,int max,String name){
        if(arg < 0 || arg > max){
            throw new IllegalArgumentException();
        }
    }
    @Override
    public boolean equals(Object o){
        if (o == this){
            return true;
        }
        if(!(o instanceof PhoneNum)){
            return false;
        }
        PhoneNum pn = (PhoneNum)o;
        return pn.prefix == prefix
                && pn.code == code;
    }
    public static void main(String []args){
        Map<PhoneNum,String> map = new HashMap<>();
        map.put(new PhoneNum(1,2),"jenny");
        System.out.println(map.get(new PhoneNum(1,2)));
    }
}

  • 这时候输出是null,这里涉及两个PhoneNum的实例;
  • 第一个被用于插入HashMap中,第二个实例与第一个实例相等,被用于获取;
  • 由于PhoneNum类没有覆盖hashcode方法,从而导致两个相等的实例具有不想等的散列码,违反了hashCode规约
  • 因此,put方法把电话号码对象存放在一个散列桶中
  • get方法却在另一个散列桶中查找这个电话号码,及时这两个实例正好被放到同一个散列桶中
  • get方法也必定会返回null,因为HashMap有一项优化,可将与每个项相关的散列码缓存起来
  • 如果散列码不匹配,也不必检验对象的等同性

类层次由于标签类

反例:下面类能表示圆形或者矩形

//  图形类
public class Figure {
    enum Shape{RECTANGLE, CIRCLE};

    final Shape shape;

    // 矩形
    double length;
    double width;
    // 圆形
    double radius;
    Figure(double radius){          //圆
        shape = Shape.CIRCLE;
        this.radius = radius;
    }
    Figure(double length,double width){       // 矩形
        shape = Shape.CIRCLE;
        this.length = length;
        this.width = width;
    }
    //计算面积
    double area(){
        switch (shape){
            case CIRCLE:
                return Math.PI * (radius * radius);
            case RECTANGLE:
                return length * width;
            default:
                throw new AssertionError();
        }
    }
}

  • 这种标签类有着许多缺点;充斥着样板代码,包括枚举声明、标签域;
  • 在Figure类中,只有一个这样的方法:area,这个抽象类是类层次的根

接下来进行分解上面的反例


//  抽象类
abstract class Figure {
    abstract double area();
}
// 圆
class Circle extends Figure{
    final double radius;
    Circle(double radius) { this.radius = radius; }
    double area() { return Math.PI * (radius * radius); }
}
// 矩形
class Rectangle extends Figure{
    final double lenght;
    final double width;

    Rectangle(double lenght, double width) {
        this.lenght = lenght;
        this.width = width;
    }
    double area() { return lenght * width; }
}

posted @ 2021-05-13 12:21  xiaoff  阅读(52)  评论(0编辑  收藏  举报