chenDoInG 想法太多而书读太少

learning at effect java 3

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

Tips:Singleton模式->单件模式/单例模式,有兴趣看看它的三种实现方式:急切实例化,延迟实例化,双重检查加锁实例化

使用单个元素的枚举类型来实现Singleton模式

    public enum Elvis{
        INSTANCE;
        public void leaveTheBuilding(){
              System.out.println("枚举强化单件属性");
        }
    }

优点: 1.更加简洁 2.防止多次实例化,即使是在面对复杂的序列化或者反射攻击

Tips:自行Google->java序列化和反序列化

Learning at Effect Java 2

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

原因

静态工厂和构造器有个共同的局限性:不能很好地扩展到大量的可选参数。

现行解决方法

1.使用重叠构造器:

    public class NutritionFacts{
        private NutritionFacts(int servings){ ...//初始化}
        private NutritionFacts(int servings, int calories){ ...}
        private NutritionFacts(int servings, int calories, int fat){ ...}
    }

但是这样的构造器多了许多你不想要设置的参数,还不得不为它们传递值。

缺点:非必填参数比较多时,客户端代码会很难编写,并且难以阅读。而且如果客户端不小心颠倒了其中两个参数的顺序,编译器不会出错,但是程序运行时会出现错误。

2.使用JavaBean,调用一个无参构造器来创建对象,调用setter方法来设置每个必要的参数,以及可选参数:

      public class NutritionFacts {
        private int servings = -1;
        public NutritionFacts(){}
        public setServings(int val){ servings = val;}
    }

缺点:在构建过程中JavaBean可能处于不一致的状态(不一致的状态:我的理解是必填参数可能没有设置,导致调用过程中导致报错,如果有人理解这里说的不一致的状态可以和我讲解下)。另外JavaBean阻止了把类做成不可变的可能,需要付出额外的努力来确保它的线程安全(有兴趣的可以去了解下什么叫线程安全和非线程安全)。

3.使用构建器模式:不直接生成想要的对象,让客户端利用所有必要的参数调用构造器,得到一个builder对象;然后在builder对象上调用类似于setter的方法,来设置每个相关的可选参数;最后在客户端调用无参的build方法来生成不可变对象。

      public class NutritionFacts {
        private final int servings;
        private final int calories;
        private final int fat;
       
        public static class Builder {
            private int servings = 0;
                 private int calories = 0;
            private int fat = 0;
            //传入必填参数
            public Builder(int servings,int calories){
                this.servings = servings;
                this.calories = calories;
            }
            //非必填参数
            public Builder fat(int val){
                this.fat = val;
                return this;
            }
            //返回一个不可变的NutritionFacts
            public NutritionFacts build(){
                return new NutritionFacts(this);
            }
       }
      
       private NutritionFacts(Builder builder){
             servings = builder.servings;
             calories = builder.calories;
             fat = builder.fat;
       }
 }

优点:builder像构造器一样,可以对参数强加约束条件。有多少个可变参数,就可以根据需要设置多少可变参数

缺点:构建器模式的开销比构造器大,在某些是否注重性能的情况下,可能成为问题。

总结:如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式就是种不错的选择,特别是当大多数参数都是可选的时候。与使用传统的重叠构造器相比,使用Builder模式的代码更易于阅读和编写,也比JavaBean更加安全。

Tips:为什么代码易于阅读和编写是个优点,编写代码很大情况是再理解别人写的代码逻辑和意图,甚至是自己写的,所以易于阅读的代码能提高你的编码或者别人的编码效率,推荐大家看《clean code》这本书。

learning at effect java 17

多用组合少用继承

tips:在包内部使用继承是非常安全的,子类和超类的实现都处于同一个程序员的控制。

tips:对于专门为了继承而设计,并且具有很好文档说明的类来说,使用继承也是非常安全的。

tips:适用于类扩展,不适用于接口扩展

只有当子类真正是个超类的子类型时,才适合用继承。

  1. 两个类A和B,当且仅当B is-a A时,B才应该扩展A
  2. 两个类A和B,如果B has-a A 时,B可以把A组合到自己的类里面

tips:可以参考设计模式->策略模式

优先使用组合的优点:

1.不会暴露不必要的实现细节 2.继承会把超类中的所有缺陷传播到子类中,而组合可以通过设计将这些缺陷隐藏

总结:如果一个类B只是包含了另外一个类A的某些特性时,那么使用策略模式封装类A的方法,同时可以扩展自己的方法。

Learning At Effect Java 1

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

优点:

1.他们有名称。使用具有适当名称的静态方法返回对象,代码更易于阅读

    new BigInteger(int,int,Random)->BigInteger.probablePrime(int,int,Random)
    表明返回的BigInteger可能为素数

2.不必再每次调用他们的时候都创建一个新的对象

    这使得不可变类使用预先构建好的实例,或者将构建好的实例缓存起来,重复利用,避免创建不必要的重复对象

3.可以返回原返回类型的任何子类型的对象

 public interface Provider {
      Service newService();
 }
   
 public class Services {

      // 不可实例化
      private Services() {
      }

      private static final Map<String, Provider> providers  = new ConcurrentHashMap<String, Provider>();
      private static final String                DEFAULT_PROVIDER_NAME = "<def>";

      public static void registerDefaultProvider(Provider p) {
          registerDefaultProvider(DEFAULT_PROVIDER_NAME, p);
      }

      public static void registerDefaultProvider(String name, Provider p) {
          providers.put(name, p);
      }

      public static Service newInstance() {
          return newInstance(DEFAULT_PROVIDER_NAME);
      }

      public static Service newInstance(String name) {
          Provider p = providers.get(name);
          if (p == null) {
              throw new IllegalArgumentException(
                      "No providers registered with name : " + name);
          }
          return p.newService();
      }
 }

4.创建参数化实例时,使代码更加简洁

缺点:

1.不含有public或者protected的构造器,就不能被子类化。

    举个例子,使用cglib动态代理该类时应该就会报错
    但是同样的鼓励我们“多用组合,少用继承”

2.与其他的静态方法实际上没有任何区别

    查询javadoc的时候,如何实例化该类变得非常困难

###静态方法初始化类和构造器都有各自的用处,我们需要理解他们各自的长处,因地制宜。

Effective Java

Compare to使用注意技巧

	public int compareTo(That that){
		return this.a - that.a;
	}

必须确认最小和最大的可能值域之差小于或者等于INTEGER.MAX_VALUE(2^23-1),否则不要使用这种方法。如果i是一个很大的正数,而j是一个很大的负数,那么i-j将会溢出,并返回一个负值。这样就使得compareTo方法将对某些参数返回错误的结果。而且这样的错误将难于调试,因为这样的方法对于大多数的输入值都能正常工作。