java--设计模式(7种)

设计模式

设计模式(GOF,Gound Of Four)一共有23中,这些模式是为了让我们的代码更有效率而被开发的。

1、单例模式

单例模式也就是虚拟机只有一个实例对象,好处是可以直接访问,不需要实例化该类的对象。。

代码演示

1.1饿汉式

public class Demo1 {
    //创建一个私有静态常量对象,这样子这个对象就不可以被更改,也不可以直接被访问
    private static final Demo1 d=new Demo1();
    //返回一个对象,这样可以访问到这个私有对象
    public static Demo1 getD(){
        return d;
    }
}
public class Demo1Test {
    public static void main(String[] args) {
        //获取两个对象
        Demo1 d = Demo1.getD();
        Demo1 d1 = Demo1.getD();
        //输出对象的地址,你会发现两个地址相同,因此它们是同一个对象
        System.out.println(d);
        System.out.println(d1);
    }
}

结果

org.ji21.code.Demo1@4554617c
org.ji21.code.Demo1@4554617c

1.2、懒汉式,它与饿汉式不同在于它不马上创建对象,等到调用时,在创建对象。

public class Demo2 {
    //声明对象
    private static Demo2 D;
    //获得访问对象,添加synchronized关键字,这是因为多线程时,线程一同获取该方法,
    //刚好一个线程在判断是否被实例化,还没有来得及执行实例化,执行权被另一个线程抢到,
    //它也进行判断,这时因为还没有实例化,因此也会通过if判断,这时就会创建两个对象,
    //这就违反了单例的初衷了
    public static synchronized Demo2 getD(){
    //因为只创建一个实例,因此判该对象是否已经被实例化,如果没有就实例化,要保证只有一个实例对象
        if(D==null){
            D=new Demo2();
        }
        return D;
    }
}
public class Demo2Test {
    public static void main(String[] args) {
        Demo2 d = Demo2.getD();
        Demo2 d1 = Demo2.getD();
        System.out.println(d);
        System.out.println(d1);
    }
}

结果

org.ji21.code.Demo2@4554617c
org.ji21.code.Demo2@4554617c

1.3枚举的方式(也是饿汉式)

public enum  Demo3 {
    singel;
}
public class Demo3Test {
    public static void main(String[] args) {
        Demo3 demo = Demo3.singel;
        Demo3 demo1 = Demo3.singel;
        System.out.println(demo==demo1);
    }
}

结果

true

1.4懒汉式更好的实现是用内部类私有化

public class Demo4 {
    //内部类私有式外部不能访问他,在里面创建静态对象,当类加载时,静态的成员也被加载,
    //这时就创建了一个Demo4对象,也就是说它只加载了一次,因此下面得到的对象d也就只有一个
    private static class newInstance{
        static Demo4 d=new Demo4();
    }
    //返回一个Demo4对象
    public static Demo4 getd(){
        return newInstance.d;
    }
}
public class Demo4Test {
    public static void main(String[] args) {
        Demo4 getd = Demo4.getd();
        Demo4 getd1 = Demo4.getd();
        System.out.println(getd==getd1);
    }
}

结果

true

2、享元模式(flyweight)

这个模式是用来重用已有的对象的,如前面说写过的Integer对象,它就是一个享元模式下的对象,它在-128~127之间都是在用同一个空间,只有超出了范围,它才会创建新的空间,封装的基本数据类型都有这个特点。

代码演示

public class Demo5Test {
    public static void main(String[] args) {
        //Integer它在-128~127之间是不会new新空间的,因此第一个输出是true
        Integer x1=Integer.valueOf(100);
        Integer y1=Integer.valueOf(100);
        System.out.println(x1==y1);
        //超过范围这里是false
        Integer x2=Integer.valueOf(200);
        Integer y2=Integer.valueOf(200);
        System.out.println(x2==y2);
    }
}

结果

true
false

3、原形模式(prototype)

这个模式是根据已有的对象,来创建新的对象的,也就是克隆。

代码演示

1、克隆姓名、年龄和日期

import java.util.Date;
//克隆需要继承克隆接口,重写里面的方法
public class Demo5 implements Cloneable{
    private String name;
    private int age;
    private Date date;
    //重写toString方法,方便看输出
    @Override
    public String toString() {
        return "Demo5{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", date=" + date +
                '}';
    }

    public Demo5(String name,int age,Date date) {
        this.name=name;
        this.age=age;
        this.date = date;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public Date getDate() {
        return date;
    }
    //重写克隆方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class Demo5Test {
    public static void main(String[] args) {
        Demo5 p1 = new Demo5("jack",33,new Date());
        System.out.println(p1.toString());
        Demo5 p2= null;
        try {
        //上面的重写的克隆方法返回的是Object类型,因为要给的是p2赋值,因此要转型为Demo5
            p2 = (Demo5) p1.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        System.out.println(p2.toString()) ;
    }
}

这样就克隆成功了(浅克隆),但是如果你想要改变其中某个克隆值得话,会出现一些问题,如上述的日期类,它会一起改变,因为它是引用数据类型,传过去的是地址,因此改变时,会一起改变,反之基本数据类型就不会,要想解决这个问题就要用到序列化流和反序列化流了,因为我序列化和反序列化流用来操作对象的,这样克隆到的数据会立即写入,不会像之前那样只复制链接,而值没有立即更改的情况了,这也就是深克隆。

2、深克隆

import java.io.*;
import java.util.Date;
//序列化需要实现Serializable接口
public class Demo7 implements Cloneable, Serializable{
    private String name;
    private int age;
    private Date date;

    @Override
    public String toString() {
        return "Demo7{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", date=" + date +
                '}';
    }

    public Demo7(String name, int age, Date date) {
        this.name = name;
        this.age = age;
        this.date = date;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public Date getDate() {
        return date;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //读入字节流
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try {
            //把当前对象写入序列化流
            new ObjectOutputStream(byteArrayOutputStream).writeObject(this);
            //把字节流变为字节数组
            byte[] bytes = byteArrayOutputStream.toByteArray();
            //把字节数组入读字节流中,也就是读其中的数据
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            //用反序列流读取字节流读入的数据
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
            return objectInputStream.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}
import java.util.Date;

public class Demo7Test {
    public static void main(String[] args) {
        Demo7 p1 = new Demo7("jack", 40, new Date());
        System.out.println(p1.toString());
        try {
            Demo7 p2=(Demo7) p1.clone();
            //获取日期对象,并把其中的小时改为8
            p2.getDate().setHours(8);
            System.out.println(p2.toString());
        } catch (CloneNotSupportedException e) {
            //e.printStackTrace();
        }
    }
}

结果

Demo7{name='jack', age=40, date=Thu Nov 29 10:56:17 CST 2018}
Demo7{name='jack', age=40, date=Thu Nov 29 08:56:17 CST 2018}

4、策略模式(Strategy)

这个模式是用来策划java中的集合或数组的排序算法,对于规模小的使用的是插入排序,基本数据类型用的是双基点快速排序,Arrays.sort()和Collections.sort(),它们底层实现都是TimSort实现的,但是排序算法并不是固定的,它会根据程序员的需求而改变,不过你需要自己去实现排序算法,这个时候就用到了比较器(Comparator)。

代码演示

public class Demo6Test {
    public static void main(String[] args) {
        int[] a={4,3,9,7,6};

        System.out.println("排序前");
        //直接用Arrays的toString方法可以直接输出数组
        System.out.println(Arrays.toString(a));
        //Arrays.sort()的参数只能是数组,基本数据类型Object类型的数组
        Arrays.sort(a);
        System.out.println("排序后");
        System.out.println(Arrays.toString(a));

        List<Integer> list = new ArrayList<>();
        list.add(5);
        list.add(3);
        list.add(9);
        list.add(4);
        list.add(6);
        list.add(7);
        list.add(8);

        System.out.println("排序前");
        ListIterator<Integer> iterator = list.listIterator();
        while (iterator.hasNext()){
            Integer next = iterator.next();
            System.out.print(next+" ");
        }

        System.out.println("");
        //Collections的sort方法里填的参数只能是List集合
        Collections.sort(list);
        System.out.println("排序后");
        ListIterator<Integer> iterator2 = list.listIterator();
        while (iterator2.hasNext()){
            Integer next = iterator2.next();
            System.out.print(next+" ");
        }
    }
}

结果

排序前
[4, 3, 9, 7, 6]
排序后
[3, 4, 6, 7, 9]
排序前
5 3 9 4 6 7 8 
排序后
3 4 5 6 7 8 9 

5、迭代器模式(iterator)

这种模式不需要对集合内部构造进行了解,直接遍历元素,这个迭代器经常在Collection集合中用到,上面那个排序的例子就用到了迭代器。

6、建造器模式

建造器模式就入之前说过的stringBuffe类,里的append它就用到了构造器,可以用逗号添加元素,而且构造器可以选择你想填的内容,不写的内容默认空值,也可以无序调用,这就方便了我们想给某个属性赋值。

代码演示

public class Demo6 {
    private String name;
    private int age;
    private String sex;
    //重写toString方法
    @Override
    public String toString() {
        return "Demo6{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
    //私有构造方法使外界不能直接访问它,这样就不需要实例化了
    private Demo6(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    //建造构造器,其实构造器就是一个静态的内部类,当外部类加载时,它也被加载,
    //到时就直接用外部类调用它,那也就可以调用它里面的方法了
    public static class Bulider {
        private String name;
        private int age;
        private String sex;
        //里面的方法返回的是该类,这样调用完一个方法,返回该类,又可以继续调用方法(用类名.方法名的方法)
        public Bulider Name(String name) {
            this.name=name;
            return this;
        }

        public Bulider Age(int age) {
            this.age=age;
            return this;
        }

        public Bulider Sex(String sex) {
            this.sex=sex;
            return this;
        }
        //最后在该类中返回一个外部类的实例,这样外面的私有成员变量,就被赋值了,重写的toString方法获得这些值,就可以按格式输出了
        public Demo6 person() {
            return new Demo6(this.name,this.age,this.sex);
        }
    }

}

结果

Demo6{name='null', age=50, sex='male'}
Demo6{name='lucy', age=20, sex='null'}

7、工厂模式

这种设计模式,它提供了一种创建对象的最佳方式,在模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

代码演示

工厂中制造篮球、娃娃、笔,客户从中拿到相应的对象

创建一个接口

public interface Production {
    public abstract void production();
}

实现接口,从工厂中拿到制造好的铅笔

public class Pen implements Production{
    @Override
    public void production() {
        System.out.println("从工厂中拿到铅笔了");
    }
}

实现接口,从工厂中拿到制造好的篮球

public class Basketball implements Production{
    @Override
    public void production() {
        System.out.println("我从工厂中拿到了篮球");
    }
}

实现接口,从工厂中拿到制造好的娃娃

public class Doll implements Production{

    @Override
    public void production() {
        System.out.println("我从工厂中拿到了娃娃");
    }
}

通过输入的字符串来判断应该创建哪一个对象

public class Factory {
    public Production getPro(String prodection){
    //判断这个方法的参数是否是那几个产品,如果是就返回相应的产品,否则就返回null,equalsIgnoreCase表示忽略字符串大小写
        if(prodection==null){
            return null;
        }else if(prodection.equalsIgnoreCase("doll")){
            return new Doll();
        }else if(prodection.equalsIgnoreCase("basketball")){
            return new Basketball();
        }else if(prodection.equalsIgnoreCase("pen")){
            return new Pen();
        }
        System.out.println("工厂中没有这个产品");
        return null;
    }
}

测试类

public class FactoryTest {
    public static void main(String[] args) {
        //创建工厂对象,调用方法,方法的返回值是相应的对象,那就会调用相应对象的方法,从这里可以看出,我们并不是直接把相应的对象new出来而是通过工厂返回的对象来调用对象的方法的
        Factory factory = new Factory();
        factory.getPro("Doll").production();
        factory.getPro("basketball").production();
        factory.getPro("pen").production();
    }
}

结果

我从工厂中拿到了娃娃
我从工厂中拿到了篮球
从工厂中拿到铅笔了

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值