则不达的Java专题

1 Java对象管理

2016-12-04  本文已影响0人  则不达
  则不达的博客从这篇文章开始,我想说说我为什么写博客,其实之前也尝试过写博客,但都没有坚持下来,但这回找工作跳槽比较从容,再加上做安卓开发的这三年,都没有把知识体系好好整理一下,一直都是忙工作,正应了一个哥们说的那句话,一年经验重复了三年,当初定好的一天一点进步的目标也没有实现,所以为了给之前三年一个交代,也给之后三年一个好的开端,所以我从今天开始写文章,这些文章都是一边学一边整理,所以更像是一份笔记。

本节重点说对象的创建和管理,如果还有什么这方面的欠缺,欢迎留言。

Effective Java这本书里,作者一直在申明一条原则:
对象不能在初始化过程中,还能被访问到,
必须准备好所有必要字段,再获取对象,
不要留中间状态。

1 静态工厂

这个比较简单,先上代码:

1.1 套路


//常规模式
public static class Boolean{

    //静态工厂方法
    public static Boolean valueOf(boolean b){
        return new Boolean(b); 
    }

    private boolean v;
    
    private Boolean(boolean b){
        v = b;
    }
    
}

//高级模式:限制对象的数目,比如单例,此处的例子是:Boolean基本就只能对应两个值
public static class Boolean{
        
    //预定义的两个对象,某种意义上算是缓存,使用时可以省去创建对象的过程
    public static final Boolean True = new Boolean(true);
    public static final Boolean False = new Boolean(false);
    
    //静态工厂方法
    public static Boolean valueOf(boolean b){
        return b ? True : False;
    }

    private boolean v;
    
    private Boolean(boolean b){
        v = b;
    }
    
}

2 服务提供者框架

这是从静态工厂好处3里引出来的,对外提供一个接口,客户端依赖于此接口实例,但并不关心具体实现


/**
 * ================服务接口:Service Interface==================
 * 一个对外提供服务的接口,并且不同情况,会产生不同的Service对象,
 * 即通过Service的不同实现,对外提供不同的服务
 *
 */
public interface Service {
    
    void doService();

}


/**
 * ================服务提供者接口==================
 * 用来生成Service对象,注意,如果不使用Provider,则注册到Services的就得是Service实现类的Class对象,
 * newInstance也只能通过反射来了
 * 问题就是Provider实现类应该有几个
 *
 */
public interface Provider {
    Service newService();
}

public class Services {

    private Services(){}
    
    //================提供者注册API==================//
    //这里要么注册provider对象,要么注册Service实现类的Class,你选吧
    private static final Map<String, Provider> providers = new ConcurrentHashMap<>();
    public static final String DEFAULT_PROVIDER_NAME = "<def>";
    
    public static void registerDefaultProvider(Provider p){
        registerProvider(DEFAULT_PROVIDER_NAME, p);
    }

    public static void registerProvider(String defaultProviderName, Provider p) {
        providers.put(defaultProviderName, p);
    }
    
    //================服务访问API==================//
    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 provider registered with name + " + name);
        }
        return p.newService();
    }
}

3 Builder模式的构建器

3.1 简单模式

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;
    
    
    public NutritionFacts(Builder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
    
    public static class Builder{
        
        //必填的参数,无默认值
        private final int servingSize;
        private final int servings;
        
        //选填的参数,有默认值
        private int calories = 0;
        private int fat      = 0;
        private int carbohydrate = 0;
        private int sodium = 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 carbohydrate(int val){ carbohydrate = val; return this;}
        public Builder sodium(int val){ sodium = val; return this; }
    
        public NutritionFacts build(){
            return new NutritionFacts(this);
        }
        
    }
    
}

public static void main(String[] args) {
    NutritionFacts cocacola = new NutritionFacts.Builder(240,  8)
            .calories(100)
            .sodium(35)
            .carbohydrate(27)
            .build();
}


3.2 Builder接口模式

将Builder抽取出来单独做一个接口,这个接口的对象可以:

public interface Builder<T> {
    public T build();
}


public class NutritionFacts2 {

    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 NutritionFacts2(MyBuilder builder) {
        servingSize = builder.servingSize;
        servings = builder.servings;
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
    
    public static class MyBuilder implements Builder<NutritionFacts2>{
        
        //必填的参数,无默认值
        private final int servingSize;
        private final int servings;
        
        //选填的参数,有默认值
        private int calories = 0;
        private int fat      = 0;
        private int carbohydrate = 0;
        private int sodium = 0;
        
        public MyBuilder(int servingSize, int servings){
            this.servingSize = servingSize;
            this.servings = servings;
        }
        
        public MyBuilder calories(int val){ calories = val; return this; }
        public MyBuilder fat(int val){ fat = val; return this; }
        public MyBuilder carbohydrate(int val){ carbohydrate = val; return this;}
        public MyBuilder sodium(int val){ sodium = val; return this; }
    
        public NutritionFacts2 build(){
            return new NutritionFacts2(this);
        }
        
    }
    
    public static void main(String[] args) {
        Builder<NutritionFacts2> builder = new NutritionFacts2.MyBuilder(240,  8)
                .calories(100)
                .sodium(35)
                .carbohydrate(27);
        
        NutritionFacts2 cocacola = builder.build();
    }
}

/*
这里的Builder<NutritionFacts2> builder对象,可以传给任意的抽象工厂方法
*/

4 单例模式

4.1 饿汉:公有域,或静态工厂

public class Singleton {
    
    public static final Singleton INSTANCE = new Singleton();
    private Singleton(){}
    private Object readResolve(){ return INSTANCE; }
    
    public void provideService(){
        
    }

}

//访问
Singleton.INSTANCE.provideService();
public class Singleton {
    
    private static final Singleton INSTANCE = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){ return INSTANCE; }
    private Object readResolve(){ return INSTANCE; }
    
    public void provideService(){
        
    }

}

//访问
Singleton.getInstance().provideService();

4.2 懒汉:双保险模式

这里注意延迟加载和volatile的使用

public class Singleton{
   private volatile static Singleton instance = null;
   private Singleton(){}
   public static Singleton getInstance(){
     if(instance == null){
        synchronized(Singleton.class){
            if(instance == null){ instance = new Singleton(); }
        }
     }
     return instance;
   }
}

这双重检查的概念可以推广到所有需要延迟加载的地方,如果一个变量需要延迟加载,
那访问的时候,你就应该这样:


private volatile T field;

T getField(){
    //用到了result这个局部变量,确保field只在已经被初始化的情况下读取一次,可以提升性能(非必须)
    //据说 速度比不用局部变量快了25%
    T result = field;  
    if(result == null){
        synchronized(this){
            result = field;
            if(result == null){
                field = result = computeFieldValue();
            }
        }
    }
    return result;
}

4.3 懒汉:内部类模式

这里的说法是:
SingletonHolder作为一个内部类,会在访问时被加载,所以这里实现了延迟加载,并且内部类可以从语言层面上防止多线程的问题,比双重锁模式优雅的多。

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

访问Singleton这个类时,才加载Inner,单例才被初始化
而且一个Inner保证了有一个singleton静态实例
那能保证Inner只有一个吗?能啊,ClassLoader会保证Inner的Class就一个

4.4 枚举

按照Effective Java书里说法,这个方法虽然没流行起来,但最符合单例的需求

//直接就能防止反射,防止序列化时生成新类
public enum Singleton {
    
    INSTANCE;
    
    public void provideService(){
        
    }

}

4.5 暴力反射版的单例:趣味探索

public class Singleton {
    private Singleton(){}
    public void doSth(){
        System.out.println("做点什么");
    }
}
public class SingletonFactory {
    private static Singleton singleton;
    //===只实例化一次,使用暴力反射
    static{
        try {
            Class cls = Class.forName(Singleton.class.getName());
            Constructor cons = cls.getDeclaredConstructor();
            cons.setAccessible(true);
            singleton = (Singleton) cons.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static Singleton getSingleton(){
        return singleton;
    }
    
    /**
     * 扩展:一个项目可以有一个单例构造器,负责生成所有单例对象,只需要传入类型,
     * 但是需要事先知道有几个单例类型
     */
}

5 简单工厂模式

场景:披萨店里生产各种披萨,需要你实现一个用户点餐的接口,用户可以选择披萨类型

先给出Pizza的类体系

public abstract class Pizza{

}

public class CheesePizza extends Pizza{

}

public class PepperoniPizza extends Pizza{

}

public class ClamPizza extends Pizza{

}

public class VeggiPizza extends Pizza{

}
//------------------------------------------------------------
//第一个例子,不用工厂模式
//------------------------------------------------------------
Pizza orderPizza(String type){
    Pizza pizza;
    if(type.equals("cheese")){
        pizza = new CheesePizza();
    }else if(type.equals("pepperoni")){
        pizza = new PepperoniPizza();
    }else if(type.equals("clam")){
        pizza = new ClamPizza();
    }else if(type.equals("veggie")){
        pizza = new VeggiPizza();
    }
    pizza.prepare();
    pizza.bake();
    pizza.cut();
    pizza.box();
    return pizza;
}
//如果需要增删pizza的种类,则这个方法的代码需要修改
//也就是需要修改已经编译好的
//不符合对修改关闭,对扩展开放的原则

//------------------------------------------------------------
//第二个例子,使用简单工厂模式
//------------------------------------------------------------
public class SimplePizzaFactory{
    
        ///方式1:传入字符串类型的type,或者int类型的type,依然需要修改,但至少代码已经都归到一个地方了
    public Pizza createPizza(String type){
        Pizza pizza;
        if(type.equals("cheese")){
            pizza = new CheesePizza();
        }else if(type.equals("pepperoni")){
            pizza = new PepperoniPizza();
        }else if(type.equals("clam")){
            pizza = new ClamPizza();
        }else if(type.equals("veggie")){
            pizza = new VeggiPizza();
        }
        return pizza;
    }
        
        ///方式2:再增加新的pizza类型,这个方法也不需要修改了
        public <T extends Pizza> T createPizza(Class<T> clazz){
        Pizza pizza;
        try{
            pizza = clazz.newInstance();
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("生成错误");
        }
        return (T)pizza;
    }

       ///方式3:参数也可以传入Builder,Provider等
    
}

public class PizzaStore{
    
    SimplePizzaFactory factory;
    public PizzaStore(SimplePizzaFactory fac){
        this.factory = fac;
    }
    
    
    public Pizza orderPizza(String type){
        Pizza pizza;
        pizza = factory.createPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
}
///

6 工厂方法

需要注意的是,工厂方法不是简单工厂的升级,
而是简单工厂所对应的需求升级了,这里的升级就是,出现了不同的产品等级
Pizza店生意好了,采用了加盟模式,NewYork, Chicago都有加盟店了,
但是这两个地方的Pizza风味有所不同,虽然同是Chesse或者Pepper,但是
还都有各自的风味,如饼的厚薄,饼的大小,传入同样的type,但返回的对象不一样了

工厂方法提供一个工厂接口或基类,由子类完成具体的创建动作,
所以工厂基类中,对对象的处理,就和子类的创建动作解耦了

工厂方法,能让各地的Pizza店还保持一样的制作流程,但各自在创建时,允许发挥个性


public abstract class PizzaStore{
        public Pizza orderPizza(String type){
        Pizza pizza;
        pizza = createPizza(type);
        pizza.prepare();
        pizza.bake();
        pizza.cut();
        pizza.box();
        return pizza;
    }
    abstract Pizza createPizza(String type);
}
public class NewYorkPizzaStore extends PizzaStore{
    
    public Pizza createPizza(String type){
        Pizza pizza;
        if(type.equals("cheese")){
            pizza = new NYCheesePizza();
        }else if(type.equals("pepperoni")){
            pizza = new NYPepperoniPizza();
        }else if(type.equals("clam")){
            pizza = new NYClamPizza();
        }else if(type.equals("veggie")){
            pizza = new NYVeggiPizza();
        }
        return pizza;
    }
    
}
public class ChicogoPizzaStore extends PizzaStore{
    
    public Pizza createPizza(String type){
        Pizza pizza;
        if(type.equals("cheese")){
            pizza = new CGCheesePizza();
        }else if(type.equals("pepperoni")){
            pizza = new CGPepperoniPizza();
        }else if(type.equals("clam")){
            pizza = new CGClamPizza();
        }else if(type.equals("veggie")){
            pizza = new CGVeggiPizza();
        }
        return pizza;
    }
    
}

Pizza的类体系还是差不多

public abstract class Pizza{

}

public class NYCheesePizza extends Pizza{

}

public class NYPepperoniPizza extends Pizza{

}

public class CGCheesePizza extends Pizza{

}

public class CGPepperoniPizza extends Pizza{

}
这里给出一段代码,还是要解决上面的需求,但不使用工厂方法,只用暴力方案

public class DependentPizzaStore{
    
    public Pizza createPizza(String style, String type){
        Pizza pizza = null;
        if(style.equals("NewYork")){
            if(type.equals("cheese")){
                return NYCheesePizza();
            }else if(type.equals("veggi")){
                return NYVeggiPizza();
            }
        }else if(style.equals("Chicago")){
            if(type.equals("cheese")){
                return CGCheesePizza();
            }else if(type.equals("veggi")){
                return CGVeggiPizza();
            }
        }
        return pizza;
    }
    
}

这段代码很重要,很直观的告诉你简单工厂和工厂方法解决的问题

createPizza(String style, String type)两个参数时,style就变成了工厂方法里的类体系结构,每个工厂子类其实就是不同的style

createPizza(String type)一个参数时,使用简单工厂就可以了

如果出现了CreatePepper, CreateSauce,需要你创建辣椒和酱料,和Pizza一起作为一个产品族,就用到抽象工厂了

7 抽象工厂

看到这里时,你应该已经了解了简单工厂和工厂方法

现在假设不同地域的pizza店,得就近生产自己地区的酱料和奶酪,以保持新鲜


public interface AbstractFactory{
    public Cheese createCheese();
    public Sauce createSauce();
}

public class NewYorkFactory extends AbstractFactory{
    public Cheese createCheese(){
        return NYCheese();
    }
    public Sauce createSauce(){
        return NYSauce();
    }
}
public class CGFactory extends AbstractFactory{
    public Cheese createCheese(){
        return CGCheese();
    }
    public Sauce createSauce(){
        return CGSauce();
    }
}


这俩工厂用在哪儿呢?注意,酱料和奶酪作为一个产品族,他们组装出来的产品就是:Pizza

所以这里的工厂,应该用在组装Pizza的地方
public class ChicogoPizzaFactory extends PizzaFactory{
    
    public Pizza createPizza(String type){
        Pizza pizza;
                AbstractFactory fac = new CGFactory();
        if(type.equals("cheese")){
            pizza = new CGCheesePizza(fac);
        }else if(type.equals("pepperoni")){
            pizza = new CGPepperoniPizza(fac);
        }else if(type.equals("clam")){
            pizza = new CGClamPizza(fac);
        }else if(type.equals("veggie")){
            pizza = new CGVeggiPizza(fac);
        }
        return pizza;
    }
    
}

8 Flyweight:蝇量模式(享元模式)

Flyweight模式就是解决外部状态的问题,用一个对象,提供很多个虚拟对象


//树的对象千千万,但不怕,这里走了个极端,树成了无状态对象,其实可以有内部状态,
//如位置xy和树龄age,都可以作为内部状态使用,而非方法参数
public class Tree{
    RealTree realTree;
    public void display(int x, int y, int age){

    }
}

///树的外部状态,其实就三个对象,柳树,杨树,槐树
//这里这几个字段说是固定不变的,可能有点牵强,不要太纠结
public class RealTree{
    public Leaf leaf;
    public Type type;
    public int 硬度;
    public int 果实;
}


public class TreeManager{
    public HashMap<String, RealTree> flyweights = ...;
    static{
      flyweights.put("杨树", new RealTree());
      ....
    }
    public RealTree getRealTree(String key){...}
    
}

9 备忘录模式

对象快照,例如在命令模式的撤销功能中,需要记住对象之前的状态,或者游戏的进度保存

备忘录模式就是由对象自己管理自己的save和restore

public class GameRole{
    
    private String currentEnemy;
    private String currentFriend;
    private int currentX, currentY;
    private int currentAttachPower;
    
    public Memento createMemento(){
        return new Memento(currentX, currentY);
    }
    
    public void setMemento(Memento memento){
        this.currentX = memento.currentX;
        this.currentY = memento.currentY;
    }
}

public static class Memento{
    private int currentX, currentY; //当前位置坐标,需要保存和还原
}

10 原型模式和深拷贝浅拷贝

深拷贝与浅拷贝问题中,会发生深拷贝的有java中的8种基本类型以及他们的装箱类型,另外还有String类型。其余的都是浅拷贝

public class Prototype implements Cloneable {  
    private ArrayList list = new ArrayList();  
    public Prototype clone(){  
        Prototype prototype = null;  
        try{  
            prototype = (Prototype)super.clone();  
            prototype.list = (ArrayList) this.list.clone();  
        }catch(CloneNotSupportedException e){  
            e.printStackTrace();  
        }  
        return prototype;   
    }  
}
上一篇下一篇

猜你喜欢

热点阅读