看《Effective Java 第三版》记录

一.静态工厂方法来取代构造器

例子来自Boolean这个类,这个valueOf方法返回了一个Boolean实例,但是它并不是Boolean的构造方法,这样做有几个好处:

    public static Boolean valueOf(boolean b) {
        return (b ? TRUE : FALSE);
    }
  1. 有名称,可以自己定义
  2. 不用每次都新建一个对象,比如上面这个方法就不是每次都新建一个,是在类的成员变量“缓存”了两个:在这里插入图片描述
  3. 更灵活,可以返回原本类型的任意一个子类对象,如果用构造器只能返回原本类型对象
  4. 每次调用,返回对象都可以变化,根据不同参数返回不同对象,而不是像构造器一样,每次调都返回一样的对象,Java.util.EnumSet就是一个例子
  5. 方法返回的对象所属的类,在编写构造方法的类时,可以不存在,这就是一种服务提供者
    框架,参考Java.util.ServiceLoader

看到这发现平时也经常用啊,比如Collections.emptyList()这种



二.建造者模式来取代一般的构造器,应对参数过多的问题

问题:
一般的构造器,可能会遇到有很多参数的情况,比如下面这种,很多参数我不想要,但是必须得set,就很不方便

在这里插入图片描述
解决:采用建造者Builder模式
它不直接构造对象,而是先得到一个Builder对象,然后在这个对象上调用类似于Setter的方法
结果是这样的,是一个流式调用的API

NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8).calories(100).sodium(35).carbohydrate(27).build();

其实现是,得有一个静态内部类叫做Builder

public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    //1.静态内部类Builder
    public static class Builder {
        // Required parameters
        private final int servingSize;
        private final int servings;

        // Optional parameters - initialized to default values
        private int calories      = 0;
        private int fat           = 0;
        private int sodium        = 0;
        private int carbohydrate  = 0;


		//2.Builder的这些set方法,返回的对象得是Builder,这样才能穿起来
        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings    = servings;
        }

        public Builder calories(int val)
        { calories = val;      return this; }
        public Builder fat(int val)
        { fat = val;           return this; }
        public Builder sodium(int val)
        { sodium = val;        return this; }
        public Builder carbohydrate(int val)
        { carbohydrate = val;  return this; }

        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }

	//3.还得有一个返回目标对象的构造方法,参数是Builder
    private NutritionFacts(Builder builder) {
        servingSize  = builder.servingSize;
        servings     = builder.servings;
        calories     = builder.calories;
        fat          = builder.fat;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }

三.用私有构造器和枚举强化类的单例属性(Singleton)

单例的意思就是只被实例化一次,通常那些无状态的类需要是单例的,因为创建多个对象没有必要!

第一种做法:静态成员是Final类型的


// Singleton with public final field  (Page 17)
public class Elvis {
    public static final Elvis INSTANCE = new Elvis();

    private Elvis() { }

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.INSTANCE;
        elvis.leaveTheBuilding();
    }
}

第二种做法:静态工厂方法
跟上面的几乎一样,就是多了个getInstance方法

// Singleton with static factory (Page 17)
public class Elvis {
    private static final Elvis INSTANCE = new Elvis();
    private Elvis() { }
    public static Elvis getInstance() { return INSTANCE; }

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.getInstance();
        elvis.leaveTheBuilding();
    }
}

第三种做法:枚举

// Enum singleton - the preferred approach (Page 18)
public enum Elvis {
    INSTANCE;

    public void leaveTheBuilding() {
        System.out.println("Whoa baby, I'm outta here!");
    }

    // This code would normally appear outside the class!
    public static void main(String[] args) {
        Elvis elvis = Elvis.INSTANCE;
        elvis.leaveTheBuilding();
    }
}

四.用私有构造器和枚举强化类的单例属性(Singleton)

有的类不需要构造器,只需要用它的静态方法和静态变量,比如Collections,Arrays这种工具类。这种类不需要被实例化,但是如果不写的话,编译器会自动提供一个公有的default constructor,所以需要写一个私有的构造器

//java util包的Collections就是这样写的
public class Collections {
    // Suppresses default constructor, ensuring non-instantiability.
    private Collections() {
    }
    xxxxxxxx
    }

五.依赖注入

如果一个类依赖了底层资源,需要通过依赖注入的方式来编写类,也就是说构造器,需要有一个参数,根据不同参数set不同的值,就是这么简单。


六.避免重复创建对象

比如多用静态工厂方法Boolean.valueOf而不是每次new 一个boolean() ,但是有时候非常隐蔽,你不看底层代码看不出来它有没有创建对象,比如书里给了一个正则匹配的例子:

第一种做法,这种不好,因为String.matches会在里面创建Pattern实例
public class RomanNumerals {
    // Performance can be greatly improved! (Page 22)
    static boolean isRomanNumeralSlow(String s) {
        return s.matches("^(?=.)M*(C[MD]|D?C{0,3})"
                + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
    }

第二种做法好,它把Pattern编译成final的了
    // Reusing expensive object for improved performance (Page 23)
    private static final Pattern ROMAN = Pattern.compile(
            "^(?=.)M*(C[MD]|D?C{0,3})"
                    + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");

    static boolean isRomanNumeralFast(String s) {
        return ROMAN.matcher(s).matches();
    }

还有就是类似于Long这种包装类,会自动装箱,所以要多用基本类型。


七.消除过期的对象引用

  1. 自己管理内存的类,比如Stack栈
  2. 缓存可能有内存泄漏
  3. 监听器和回调

总的来说 看的不是特别明白


八.避免使用终结方法和清除方法

终结方法就是finalize() 清除方法我也没用过。。。


九.关闭资源要用try-with-resources

这样更简洁,而且异常信息更有价值,这个我也没怎么用过,前提条件是资源类必须实现AutoCloseable接口,比如Stream


十.equals方法覆盖时需要注意

一致性、自反性、对称性、传递性等性质,比较麻烦一般也不会有问题,文章给了几个建议:

  1. 先用==判断参数是否为对象的引用,能节省性能
  2. 用instanceof来判断类的类型是否和要比较的一致,如果不是直接返回false.

还有警告⚠️

  1. 方法的参数必须是Object!
  2. 覆盖equals时一定要覆盖hashcode
  3. 不要让equals过于智能(复杂)

十一.equals覆盖,就得覆盖hashcode

不然hashMap和hashSet不好使了,很可能让很多对象都 映射到同一个桶中,这样hashmap就退化变成链表了


十二.始终覆盖toString()

这个一般人都知道吧 跳过了


十三.谨慎的覆盖clone

平时不用,跳过了


十四.Comparable接口

public interface Comparable<T> {

    public int compareTo(T o);
}

compareTo方法的解释:

将当前对象和目标对象进行比较,如果当对象小于目标对象,返回负整数;如果二者相等返回0,如果对象大于目标对象,返回正整数,如果因为类型导致无法比较,抛出异常

想要先比较某个属性,再比较某个属性,java8有新的API

// Comparable with comparator construction methods (page 70)
    private static final Comparator<PhoneNumber> COMPARATOR =
            comparingInt((PhoneNumber pn) -> pn.areaCode)
                    .thenComparingInt(pn -> pn.prefix)
                    .thenComparingInt(pn -> pn.lineNum);

总结:每个对排序敏感的类,都需要关注Comparable接口,比较的时候,不要使用<和>,应该在装箱基本类型使用静态方法比如Integer.compare,或者在Comparator使用比较器构造方法


十五.降低程序元素的可访问性

jdk1.9提供了新的隐式访问控制,模块可以通过模块声明导出一部分包来控制访问。
其他的就是private那些,一般也没人闲得没事攻击这个⑧。。。


十八.复合优先于继承

文章中举了一个继承HashSet的类,表示继承这个操作,需要了解原本类的具体实现,否则有可能造成不正确的结果,导致得到的子类非常脆弱。

  • 复合:就是新的类其中包括现有类的一个实例,新的类自己的方法调用现有类的方法,这被称为转发,这种方式十分稳固
// Reusable forwarding class (Page 90) 这是新类
public class ForwardingSet<E> implements Set<E> {
    private final Set<E> s;
    public ForwardingSet(Set<E> s) { this.s = s; }

    public void clear()               { s.clear();            }
    public boolean contains(Object o) { return s.contains(o); }
    public boolean isEmpty()          { return s.isEmpty();   }
    public int size()                 { return s.size();      }
    public Iterator<E> iterator()     { return s.iterator();  }
    public boolean add(E e)           { return s.add(e);      }


// Wrapper class - uses composition in place of inheritance  (Page 90) 封装的类
public class InstrumentedSet<E> extends ForwardingSet<E> {
    private int addCount = 0;

    public InstrumentedSet(Set<E> s) {
        super(s);
    }

    @Override public boolean add(E e) { //用的时候只要转发就行了,实际调用的是forwadingset的方法,实际上是set的方法
        addCount++;
        return super.add(e);
    }
    @Override public boolean addAll(Collection<? extends E> c) {
        addCount += c.size();
        return super.addAll(c);
    }
 
}

同时,因为把每一个实例都包装起来了,这个InstrumentedSet类被称为包装类(wrapperclass)。 这也正是 Decorator(修饰者)模式的应用。

只有当B是A的子类的时候,就是说B is a A,这时候才能用继承

反面例子:stack栈并不是vector,但是他继承了vector这就是不对的


十九.谨慎继承,如果要继承一定得有详细文档


二十.接口优于抽象类

这个感觉正常人都知道吧

二十一.为后代设计接口

java8之后,接口增加了缺省方法,就是为了给现有的接口加方法,在之前,如果这么改会报错。加这个东西主要是为了Lambda的应用,书里的意思是,即便有缺省方法,也得好好设计接口。


二十二.接口只用于定义类型

有一种接口只有定义了一些静态的常量,private static final xxx = 。。这种作者不建议用,它会让实现了接口的类的使用者很迷惑

二十三.类层次优于标签类

标签类就是那种,里面有enum枚举,又有属性的类,这样不如写子类好


二十四.静态成员类优于非静态成员类

因为非静态的成员类,可以访问外部类的属性;所以如果定义的这个成员类,无需访问外部对象实例,那么就得加static


二十五.不要在一个文件中定义两个class

跟标题一样,就是不要在一个.java文件中,写两个class


二十六.泛型----不要用原生类型

//不要用这种,如果这个里面放入了其他类型,那么运行阶段才会报错
private final Collection dogs;
//最好用下面这种
private final Collection<Animal> dogs;

如果用原生的类型,就失去了泛型在安全性和描述性方面的优势,如果不知道集合的类型,可以用?这种无限制通配符,也就是List<?> list

下面是术语:
在这里插入图片描述


二十七.泛型----消除unchecked警告

java在类型转换时如果没有进行类型校验,会报Uncheck cast的警告,如果确定对象能转成功一般我们是不会加校验,但警告一直存在。可以在当前方法上加上@SuppressWarnings(“unchecked”)注解关闭校验。这一节就是在告诉你,要尽可能消除这些影响。实在不行的话用@SuppressWarnings(“unchecked”),但我发现现在我们用的idea都没有了 是版本问题吗?


二十八.列表优于数组

  1. 列表,比如List 这种东西有泛型擦除,这样更灵活可以随意使用;而数组在运行期这个类型也是确定的
  2. 泛型类型是可变的

数组和泛型一般不可以很好的混合使用,(现在的程序员一般都不会用数组吧)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值