JAVA基础之设计模式和枚举

版权声明:本文为博主原创文章,未经博主允许不得转载。https://blog.csdn.net/qq_37757008/article/details/81408567 https://blog.csdn.net/qq_37757008/article/details/84576973

枚举

枚举是JDK1.5版本新增的特性(泛型、For-each等如今被广泛应用的特性也是由JDK1.5时所新增的),另外到了JDK1.6后switch语句支持枚举类型;

  • 枚举的使用情况:
    有的时候一个类的对象是有限且固定的,这种情况下我们使用枚举类就比较方便;
    枚举就是将所有的有限的类对象先定义好;
  • 语法
public enum 枚举类名{
	//枚举的所有对象都必须在第一行定义好
}

比如:

public enum Sex {
    // 男性
    MALE("男"),
    // 女性
    FEMALE("女");
    
    private String cnName;
	//可以定义方法
    public String cnName() {
        return this.cnName;
    }

    // 构造方法不能是公共的
    Sex(String cnName) {
        this.cnName = cnName;
    }

}
public enum Week {
    MON, TUE, WED, THU, FRI, SAT, SUN
}

枚举类内也可以定义属性和方法,可是是静态的和非静态的

但是构造方法必须为私有的;在定义枚举类型时我们使用的关键字是enum,与class关键字类似,只不过前者是定义枚举类型,后者是定义类类型。枚举类型Day中分别定义了从周一到周日的值,这里要注意,值一般是大写的字母,多个值之间以逗号分隔。同时我们应该知道的是枚举类型可以像类(class)类型一样,定义为一个单独的文件,当然也可以定义在其他类内部,更重要的是枚举常量在类型安全性和便捷性都很有保证,如果出现类型问题编译器也会提示我们改进,但务必记住枚举表示的类型其取值是必须有限的,也就是说每个值都是可以枚举出来的,比如上述描述的一周共有七天。
同时枚举也给我们提供了更多的方法操作所定义对象;
以上面性别枚举类举例;

//获取枚举对象的序号,序号从0开始
System.out.println(Sex.MALE.ordinal());
    System.out.println(Sex.FEMALE.ordinal());

    // 把枚举对象转为字符串
    System.out.println(Sex.MALE.name());
    
    // 把字符串转为枚举对象, 虚拟机中的枚举对象只会有一份, 可以用== 来比较
    System.out.println(Sex.valueOf("MALE") == Sex.MALE);

    // 打印所有的枚举对象
    for (Sex value : Sex.values()) {
        System.out.println(value);
    }

设计模式

定义:编程中的一些套路,让我们的代码实现特定的目的,结构上更加优秀;

  • 单例模式
    单例模式(Singleton Pattern)
    定义:虚拟机中这个类只有一个实例(一个对象);
    特点:
    1、单例类只能有一个实例。
    2、单例类必须自己创建自己的唯一实例。
    3、单例类必须给所有其他对象提供这一实例。
    单例顾名思义,就只能有一个实例;因此它需要私有,然后自己去创建出这一个唯一实例;同时它又不是说就只能自己使用,它还需要给所有其他对象提供这个实例,供他们使用,因此简单单例的代码就有了大致模板;
public class Singleton1 {

    /**
     * 让构造方法私有,别人就没法创建此类的实例了
     */
    private Singleton1() {
    }
    /**
     * 自己创建这个实例
     */
    private static final Singleton1 ME = new Singleton1();

    /**
     * 获取唯一实例
     */
    public static Singleton1 getInstance() {
        return ME;
    }
}

上面我们就看出来,我们先创建了私有的构造方法,然后自行和创建他的实例;最后给一个公有方法返回我们创造的对象实例;供其他对象调用;
但是我们想一下,这种方法在一开始就创建出了唯一的单例了,可是如果我们很久不用呢?这种方法就叫饿汉式的单例模式,就不管别人用不用,在一开始就创建好实例;这种方法就不太好;
所以我们也可以在真正需要实例的时候再创建;这种方式叫懒汉式单例
但是懒汉式单例就会出现一个问题,即如果在多线程的环境下,就不一定能保证单例;
比如上面的例子如果我们的实例方法这样写

public class Singleton1 {
    private Singleton1() {
    }
    private static final Singleton1 ME = new Singleton1();
	public static Singleton2 getInstance() {
        if (ME == null) {
            ME = new Singleton2();
        }
        return ME;
    }

在我们还没有创建好 ME = new Singleton2();时,又新进来的线程判断ME还时null,这时他就会再创建一个实例;这就不是单例了,所以我们必须加锁;
普通方法加锁,就需要声明加锁的synchronized关键字;

public static synchronized Singleton2 getInstance() {
        // 当一次调用时ME == null为真, 当后续调用时ME == null为假,就不会执行创建对象的操作了
        if (ME == null) {
            ME = new Singleton2();
        }
        return ME;
    }

这样就保证了单例;
同时懒汉式的单例也有更好的实现方式;我们可以用静态内部类的方式,在我们调用方法的时候才实现单例的创建;

public class Singleton4 {
    static{
        System.out.println("Singleton4类被加载了");
    }

    private Singleton4() {
    }

    // holder 拥有, 由静态内部类创建了他的唯一实例
    private static class Holder {
        static{
            System.out.println("Holder类被加载了");
        }
        static Singleton4 ME = new Singleton4();
    }

    public static Singleton4 getInstance() {
        return Holder.ME;
    }

    public static void test() {
        System.out.println("Singleton4其它方法");
    }
}

这里我们用一个测试类看看静态内部类的单例创建效果;

public class TestSingle {
    public static void main(String[] args) {
        Singleton4.test();
        System.out.println("========================");
        Singleton4.getInstance();
        Singleton4.getInstance();
        Singleton4.getInstance();
    }
}

在这里插入图片描述
从上面的结果我们就能看出来,在没有调用获取单例的方法时,单例并没有被创建,holder拥有创建单例的功能,那部分的代码也没有执行,在我们调用后,先用houlder,执行了它的静态内部类,创建出了唯一单例;这就是懒汉模式单例的更优方式;

  • 享元模式

享元模式是提倡重用已有的对象,而不是创建新的对象。
在Integer的valueOf方法中就用到了享元模式。

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

这段代码是指如果数字在一个范围内则会返回一个存储在cache数组中的对象。即实现了对象的重复利用。

  • 原型模式 prototype
    原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

根据已有对象来创建新的对象, 克隆;
使用场景,当对象属性很多,希望新的对象的大部分属性从原有对象复制而来。

public class User implements Cloneable {
    private String name;
    private int age;
    private Date birthday; 
    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

浅拷贝和深拷贝
刚才的Cloneable实现的是浅拷贝,也就是说,对象的属性仅仅是复制了地址,没有把内容新复制一份

深拷贝是指所有的内容都得是全新的。
使用序列化流来进行深拷贝

protected Object clone() throws CloneNotSupportedException {
		//内存操作流
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            // 把自己(当前对象)写入输出流
            new ObjectOutputStream(os).writeObject(this);
            // 拿到字节数组
            byte[] bytes = os.toByteArray();
            // 内存操作流
            ByteArrayInputStream is = new ByteArrayInputStream(bytes);
            // 对象输入流
            ObjectInputStream ois = new ObjectInputStream(is);
            return ois.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

  • 建造器模式(Builder)

通过建造起模式可以使创建对象时调用构造方法更加灵活。
适用于有多个可省参数的构造方法。
例如有一个Person类,他有以下的属性
String sex 性别
String name 名字
Integer weight; 体重
Integer height; 身高
在调用构造方法时,所有属性都是可选的。如果使用一般的模式编写,则需要为所有可能都写一个构造方法。
如果改用建造器模式,代码量就会变得简单。

public class Person {
 private String name;
 private String sex;
 private Integer weight;
 private Integer height;
 @Override
 public String toString() {
     return "Person{" +
             "name='" + name + '\'' +
             ", sex='" + sex + '\'' +
             ", weight=" + weight +
             ", height=" + height +
             '}';
 }
 
 // 建造器
 public static class PersonBuilder{
     private String name;
     private String sex="男";
     private Integer weight=50;
     private Integer height;
     // 返回值类型不再是void 而是建造器类型本身
     public PersonBuilder name(String name) {
         this.name = name;
         return this;
     }
     public PersonBuilder sex(String sex) {
         this.sex = sex;
         return this;
     }
     public PersonBuilder weight(Integer weight) {
         this.weight = weight;
         return this;
     }
     public PersonBuilder height(Integer height) {
         this.height = height;
         return this;
     }
     public Person build() {
         return new Person(this.name,this.sex,this.weight,this.height);
     }
 }

 private Person(String name, String sex, Integer weight, Integer height) {
     this.name = name;
     this.sex = sex;
     this.weight = weight;
     this.height = height;
 }
 public String getName() {
     return name;
 }
 public String getSex() {
     return sex;
 }
 public Integer getWeight() {
     return weight;
 }
 public Integer getHeight() {
     return height;
 }
}

public class TestBuilder {
    // new Person.PersonBuilder() 0 170 "张三" "男"
    public static void main(String[] args) {
        Person person = new Person.PersonBuilder()
                .sex("男")
                .name("张三")
                .height(170)
                .build();
        System.out.println(person);
    }
}

使用构造器可以让调用构造方法变得简单。

  • 迭代器模式(iterator)
    提供顺序访问聚合对象元素的方法,并且不暴露其底层表现;
    定义:以一种一致的对集合内的元素进行遍历,而不用在乎集合内的数据结构
    ArrayList 数组
    LinkedList 链表
    HashSet 数组+链表
    TreeSet 二叉搜索树-》红黑树

for(Object o : 集合)

Iterator iter = 集合.iterator();
while(iter.hasNext()) {
iter.next();
}

  • 策略模式 (Strategy)

在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
java 集合或数组的排序算法
Collections.sort
Arrays.sort
基本类型 双基点快速排序
对象类型 TimSort (早期使用归并排序)
规模小 插入排序

把排序的规则抽取出来,形成比较器接口(Comparator),不同比较器的实现就称为策略

open close 开闭原则
算法不能改-- 体现的是close原则
比较器可以改 – 体现的是open原则

Collections.sort(list, (a, b) ->  a.getAge() - b.getAge() );
        System.out.println(list);

        // 按名字拍
        Collections.sort(list, (a, b) ->  a.getName().compareTo(b.getName()) );
        System.out.println(list);

猜你喜欢

转载自blog.csdn.net/qq_37757008/article/details/84576973