zoukankan      html  css  js  c++  java
  • EffectiveJava学习笔记(一)

      第一条:静态工厂方法代替构造器

      静态工厂方法是返回一个类的实例的静态方法(此处介绍的静态工厂方法并不对应设计模式中的工厂模式),例:基本类型boolean转化为包装类:

    public static Boolean valueOf(boolean b) {
        return b ? Boolean.TRUE : Boolean.FALSE;
    }

      提供静态工厂方法而不是构造方法的优势:

    1. 静态工厂方法有名称,代码易于阅读。构造方法可以重载,但是函数名同类名,无法区分具体实现功能。所以一个类需要多个相同签名的构造器时,应提供静态工厂方法,同时函数名上加以区分。
    2. 静态工厂方法不必在每次调用时都创建一个新的对象。
    3. 可以返回原类型的任何子类型对象。应用:接口返回对象,同时不使对象的类变成公有的。
    4. 返回对象的类可以随着参数不同而改变,只要是已声明返回类型的子类型都是允许的。例:EnumSet没有公有的构造器,只有静态工厂方法,在noneOf静态工厂方法中,判断底层枚举类型的大小,返回不同类型的实例,同时不会影响客户端。
      public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
              Enum<?>[] universe = getUniverse(elementType);
              if (universe == null)
                  throw new ClassCastException(elementType + " not an enum");
      
              if (universe.length <= 64)
                  return new RegularEnumSet<>(elementType, universe);
              else
                  return new JumboEnumSet<>(elementType, universe);
          }
    5. 方法返回对象的所属的类,在编写该静态方法时可以不存在。

      静态工厂方法的缺点:

    1. 类如果不含有公有的或者受保护的构造器,将不可以被继承。
    2. 静态工厂方法难以被发现。常用函数名:from、of、valueOf、instance、create、gatType、newType、type。

      第二条:遇到多个构造器参数时考虑使用构建器

      构建器解决了构造器和静态工厂方法的局限性:当一个类有大量的可选参数时,需要重写多个构造器,代码难以维护。同时也有人提出使用JavaBean的方式(即setParam()),但是这种方式有一个很大的缺陷就是,构造过程被分到了许多调用中,JavaBean可能会处于数据不一致的状态。并且JavaBean模式把类做成不可变的可能性不复存在,需要额外保证它的线程安全。

      构造器模式实现:

    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;
    
        public static class Builder {
    
            private final int servingSize;
            private final int servings;
    
            private int calories = 0;
            private int fat = 0;
            private int sodium = 0;
            private int carbohydrate = 0;
    
            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);
            }
        }
    
        private NutritionFacts(Builder builder) {
            this.servings = builder.servings;
            this.calories = builder.calories;
            this.carbohydrate = builder.carbohydrate;
            this.fat = builder.fat;
            this.sodium = builder.sodium;
            this.servingSize = builder.servingSize;
        }
    
        NutritionFacts nutritionFacts = new NutritionFacts.Builder(10,10)
                .calories(5)
                .carbohydrate(5)
                .fat(100)
                .sodium(7)
                .build();
    }

      第三条:用私有构造器或者枚举类型强化Singleton属性

      Singleton是指仅被实例化一次的类,通常用来代表一个无状态的对象,或本质上唯一的系统组件,实现Singleton的常用方式是将构造器变为私有。如下两种方式:

    class Singleton{
    
        public static final Singleton INSTANCE = new Singleton();
    
        private Singleton(){ }
    }
    
    class Singleton2{
    
        private static final Singleton2 INSTANCE2 = new Singleton2();
    
        private Singleton2(){ }
    
        public static Singleton2 getInstance(){
            return INSTANCE2;
        }
    }

      在方法一中,如果客户端使用setAccessible(true)方法调用私有的构造方法,实例的唯一性将无法保证,此时可以在构造器中判断,如已存在当前类的实例则抛出异常。如下:

    public class SingletonDemo {
    
        public static void main(String[] args) throws Exception {
    
            Singleton singleton1 = Singleton.INSTANCE;
            Singleton singleton2 = Singleton.INSTANCE;
            Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
            constructor.setAccessible(true);
            Singleton singleton3 = constructor.newInstance();
            System.out.println(singleton1 + "~" + singleton2 + "~" + singleton3);
        }
    }
    
    class Singleton{
    
        public static final Singleton INSTANCE = new Singleton();
    
        private Singleton(){
            if(Objects.nonNull(INSTANCE)){
                throw new RuntimeException("实例已存在!!");
            }
        }
    }

       如果要将Singleton类支持序列化,仅仅实现Serializable接口是不够的,为了保证每次反序列化时不构造一个新的Singleton对象,需要提供readResolve方法。

      第三种也是最佳的实现Singleton类的方法是声明一个包含单个元素的枚举类型,这种方式更加简洁,并且提供了序列化机制,避免重复生成实例。

  • 相关阅读:
    Sqlserver 实际开发中表变量的用法
    Python Day 20 面向对象 (面向对象的组合用法,面向对象的三大特性
    Python Day 19 面向对象(初识面向对象)
    Python Day 18 常用模块(模块和包)
    Python Day 17 常用模块(常用模块一 时间模块,random模块,os模块,sys模块,序列化模块)
    Python Day 15 函数(递归函数、二分查找算法)
    Python Day 14 函数(内置函数,匿名函数(lambda表达式))
    Python Day 13 函数(迭代器,生成器,列表推导式,生成器表达式)
    Python Day 11 + Python Day 12 函数(函数名的应用,闭包,装饰器)
    Python Day 10 函数(名称空间,作用域,作用域链,加载顺序等; 函数的嵌套 global,nonlocal)
  • 原文地址:https://www.cnblogs.com/youtang/p/11409561.html
Copyright © 2011-2022 走看看