• 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类的方法是声明一个包含单个元素的枚举类型,这种方式更加简洁,并且提供了序列化机制,避免重复生成实例。

  • 相关阅读:
    redis4.0 安装
    MYSQL第一节:Mysql5.7 通用二进制安装方法
    mysql密码忘记,重新设置密码
    selenium使用
    数据结构
    Mysql数据库引擎的区别
    python中的mysql转义字符串(向mysql插入特殊字符)
    scrapyd使用
    liunx部署redis
    Django+uwsgi部署到liunx服务器上
  • 原文地址:https://www.cnblogs.com/youtang/p/11409561.html
Copyright © 2020-2023  润新知