effective java第二章

用静态工厂替代构造函数

1、关于可以重复使用对象以及命名的优势理解,但是其中可以返回原返回类型的任何类型,而该返回可以是非公有的。
如下面的例子,unmodifiableSet本身对外声明的返回类型是set接口,但是实际返回的是UnmodifiableSet这样一个非共有的类型。
也就是其实对于静态的工厂函数可以返回一个接口,具体的返回可以是该接口的任何子类,甚至是非公有,这样通过返回类型就明确是什么类型,但是又不关系具体到底是哪个实现类,足够灵活。

    public static <T> Set<T> unmodifiableSet(Set<? extends T> s) {
        return new UnmodifiableSet<>(s);
    }

    /**
     * @serial include
     */
    static class UnmodifiableSet<E> extends UnmodifiableCollection<E>
                                 implements Set<E>, Serializable {
        private static final long serialVersionUID = -9215047833775013803L;

        UnmodifiableSet(Set<? extends E> s)     {super(s);}
        public boolean equals(Object o) {return o == this || c.equals(o);}
        public int hashCode()           {return c.hashCode();}
    }

2、java8中允许接口中存在静态方法以及静态成员,但是不能是private的,直接通过接口实现调用。

//接口
public interface Eat {
    public static void getGirl() {
        System.out.println("111");
    }
}
//接口实现类
public class EatImpl implements Eat{
}

public class Main {
    public static void main(String[] args) {
        Eat.getGirl();//可以——接口调用
        EatImpl.getGirl();//不可以
    }
}

注:
(1)实现类并没用继承该静态的函数或者静态成员
(2)在java8中很多接口也存在静态方法,比如Compator接口中的comparing()
(3)接口中不能存在私有的成员以及私有的方法—接口是约束,实现类无法看到
(4)java9中可以存在私有的静态方法—允许该接口中其他静态方法使用,但还是不能有私有的成员。

3、对于服务提供api用于提供服务的静态方法,可能由于该提供者并没有注册而不能提供服务。
和服务提供框架(SPF)相关,存在四个组件:服务、服务提供者、服务注册、服务提供api:

//服务
public interface Service {
    public void service();
}
//服务提供者
public interface Provider {
    public Service getService();
}
public class ServiceManager {
    static Map<String,Provider> map=new HashMap<>();
    //服务注册
    public static void registerProvider(String name,Provider provider){
        map.put(name,provider);
    }

    //服务提供api
    //(1)服务实现类  (2)提供者实现类   (3)提供者注册   (4)提供服务
    public static Service getService(String name){
        Provider provider = map.get(name);
        return provider.getService();
    }
}

当前实现一个服务,并提供服务仅仅需要:
(1)服务实现类
(2)提供者实现类
(3)提供者注册
(4)提供服务

即在没有任何服务提供者注册时候,是无法提供服务的,因此对于提供服务的getService()这个静态方法,可能由于没有相应的服务注册者注册,而无法提供服务。

服务提供者框架实现了服务提供者注册即可提供服务,而获取服务也直接可以通过服务提供者名字实现灵活获取

4、类如果不含共有的或者受保护的构造器就无法子类化
这个的意思是,其实静态工厂方法的本质是,通过返回这个函数返回值的子类,实现灵活性。但是由于子类继承父类,子类的构造函数会默认调用父类的无参构造函数,如果没有就需要显示调用同参的父类构造函数,因此如果父类的构造函数不是共有的或者protected那么就无法实例化子类,那么静态工厂方法就没有意义。

使用构建器方式

一般当构造对象的时候涉及多个参数,一般情况下会采用“重叠”的方式构建对象,即通过不同个数的构造器,创建不同的对象;或者通过javaBean的方式,先构建没有参数的对象,再通过setter的方式填充参数,但是此种方式会造成不一致,即这边还没有构建完成,那边就使用就会不一致。

通过构造器的方式进行构建,即先构建Buillder,而Builder中的参数和实际需要构建的对象的参数是一一对应的:

(1)首先,通过必要参数使用Builder的构造器构造Builder
(2)通过函数,基于需要,填充参数
(3)build对象

public class Subject {
    int id;
    String name;
    String teacherName;
    int score;
    static class Builder{
        int bId;
        String bName;
        String bTeacherName;
        int bScore;

        public Builder(int id,String name){
            bId=id;
            bName=name;
        }

        public Builder buildTeacherName(String teacherName){
            bTeacherName=teacherName;
            return this;
        }

        public Builder buildScore(int score){
            bScore=score;
            return this;
        }

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

    private Subject(Builder builder){
        this.id=builder.bId;
        this.name=builder.bName;
        this.teacherName=builder.bTeacherName;
        this.score=builder.bScore;
    }
}
new Subject.Builder(1,"java").buildScore(100).buildTeacherName("jjw").build();

通过构建器的方式保证了可读性,也保证了一致性,因为当仅当build的时候,该对象才存在。

用私有构造器

1、创建单例都需要将构造函数私有化,通过将域公有化或者通过静态方法返回单例对象。
2、单例的序列化需要实现readResolve()方法保证单例对象的唯一性:

public class Singleton  implements Serializable{
    private  static final Singleton instance=new Singleton();

    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
    private Object readResolve(){
        return instance;
    }
}
    public static void main(String[] args) throws IOException, ClassNotFoundException {

        ByteArrayOutputStream byteArrayOs = new ByteArrayOutputStream();
        ObjectOutputStream objectOs = new ObjectOutputStream(byteArrayOs);
// 对象写入流中
        objectOs.writeObject(Singleton.getInstance());

        ObjectInputStream objectIs = new ObjectInputStream(new ByteArrayInputStream(byteArrayOs.toByteArray()));
        System.out.println(objectIs.readObject() == Singleton.getInstance());
    }

上面的例子,当仅仅当readResolve()存在才会是true。

3、也可以通过枚举类返回唯一对象。

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

1、通过cleaner注册对象以及对应的State的run()方法,当对象可以进行垃圾回收时,Cleaner 类的对象会自动得到通知,执行State中的run方法——但是不能保证一定执行以及执行时机。

public class CleanerTest {
   public static void main(String args[]) {
      System.out.println("nhooo");
       //创建Cleaner
      Cleaner cleaner = Cleaner.create();
      if(true) {
         CleanerTest myObject = new CleanerTest();
          //向cleaner注册某个对象以及State,当前面对象进行垃圾回收的时候就会执行state中的run方法
            cleaner.register(myObject, new State());    // register cleaner      }
      for(int i = 1; i <= 10000; i++) {
         String[] largeObject = new String[1000];
         try {
            Thread.sleep(1);
         } catch(InterruptedException e) {
              e.printStackTrace();
         }
      }
   }
   private static class State implements Runnable {
      public void run() {
         System.out.print("Cleaning action");
      }
   }
}

try-with-resource

首先被自动关闭的资源需要实现Closeable或者AutoCloseable接口,因为只有实现了这两个接口才可以自动调用close()方法去自动关闭资源,将要关闭的外部资源在try()中创建,catch()捕获处理异常。

        try(FileInputStream inputStream=new FileInputStream(new File("test"))){
            System.out.println(inputStream.read());
        }catch (IOException e){
            throw new RuntimeException(e.getMessage(),e);
        }

将inputStream在使用完毕后关闭,就放在try中。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值