Java中常用的设计模式代码与理解

一、单例模式

1.饿汉式 (太饿了,类加载的时候就创建实例)

/**
* 饿汉式单例模式
*/
public class HungrySingleInstance {
// 在类加载时生成一个实例
private final static HungrySingleInstance instance = new HungrySingleInstance();
//因为类默认会有一个公有的无参构造,所以用私有的将其覆盖
private HungrySingleInstance(){} //调用此方法,返回类加载时生成的单例对象
public static HungrySingleInstance getInstance(){
return instance;
} }

 所谓饿汉式单例设计模式,就是将类的静态实例作为该类的一个成员变量,也就是说在 JVM 加载它的时候就已经创建了该类的实例,因此它不会存在多线程的安全问题。

但是提前对实例进行了初始化或者说构造,如果此实例又没有使用到,就会造成资源的浪费。

2.懒汉式 (太懒了,不主动创建实例,当第一次调用时,才创建)

/**
* 懒汉式单例模式
*/
public class LazySingleInstance {
// 在类加载时生成一个引用为null的实例
private static LazySingleInstance instance = null;
//因为类默认会有一个公有的无参构造,所以用私有的将其覆盖
private LazySingleInstance(){ }
public static LazySingleInstance getInstance(){
//当实例不为空,就直接返回此实例
if(null == instance){
//锁+判断,避免当多个线程同时通过上面那个判断时,创建多个实例,造成安全问题
synchronized (LazySingleInstance.class){
//只有第一次调这个方法时,创建实例
if(null == instance) {
instance = new LazySingleInstance();
}
}
}
return instance;
}
}

  懒汉式单例模式就同时解决了效率与安全的问题。

单例模式常用于以下场景:

1.经常创建又销毁的对象;

2.创建需要消耗很多资源,又经常使用的对象;

3.常用来访问数据库或文件的对象。

二、工厂模式

1.简单工厂模式

实例:一个简单的发送短信邮件的工厂模式。

/**
* 一个发送接口,用于发送短信或邮件
*/
public interface Send {
void send();
}
/**
* 邮件类实现发送接口,发出邮件
*/
public class Mail implements Send{ @Override
public void send() {
System.out.println("this is a Mail message!");
}
}
/**
* 短信类实现发送接口
*/
public class Sms implements Send{
@Override
public void send() {
System.out.println("this is a Sms message!");
}
}
/**
* 发送工厂类
*/
public class SendFactory {
/**
* 根据参数type的不同,生产不同的实例对象
* @param type
* @return
*/
public Send produce(MessageSenderType type){
if(MessageSenderType.SMS.equals(type)){
return new Sms();
}else if(MessageSenderType.MAIL.equals(type)) {
return new Mail();
}
return null;
}
}
/**
* 发送类型枚举类
*/
public enum MessageSenderType {
//短信
SMS,
//邮箱
MAIL
}
/**
* 工厂测试类
*/
public class FactoryTest { public static void main(String[] args) {
SendFactory factory = new SendFactory();
factory.produce(MessageSenderType.SMS).send();
factory.produce(MessageSenderType.MAIL).send();
}
}

简单工厂模式当需要增加产品时,要修改源码,破坏ocp原则(对扩展开放,修改封闭)。

2.工厂方法模式

  工厂方法模式与简单工厂模式差不多,区别在于工厂方法模式的工厂类,由多个方法组成,每个方法用来生产一个相应的对象。而简单工厂模式,是在一个方法中,根据传入的参数不同也生产不同的对象。所以他们的局限性也都一样。

3.抽象工厂模式

  抽象工厂模式是所有形态的工厂模式中最为抽象和一般性的,抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,创建多个产品族的产品对象。具有比较好的扩展性,但要写较多的类和接口,前两种工厂模式理解上会困难一些。

package abstractFactory;

public abstract class Apple implements Fruit {

    public abstract void get();
} package abstractFactory; public abstract class Banana implements Fruit { public abstract void get();
} package abstractFactory; public interface Fruit { public void get();
} package abstractFactory; public interface FruitFactory {
//实例化Apple
public Fruit getApple(); //实例化Banana
public Fruit getBanana();
} package abstractFactory; public class InnerApple extends Apple {
@Override
public void get() {
System.out.println("长在室内的苹果");
}
} package abstractFactory; public class InnerBanana extends Banana {
@Override
public void get() {
System.out.println("长在室内的香蕉");
}
} package abstractFactory; public class InnerFruitFactory implements FruitFactory {
@Override
public Fruit getApple() {
return new InnerApple();
} @Override
public Fruit getBanana() {
return new InnerBanana();
}
} package abstractFactory; public class NorthApple extends Apple { @Override
public void get() {
System.out.println("长在北方的苹果");
}
} package abstractFactory; public class NorthBanana extends Banana {
@Override
public void get() {
System.out.println("长在北方的香蕉");
}
} package abstractFactory; public class NorthFruitFactory implements FruitFactory {
@Override
public Fruit getApple() {
return new NorthApple();
} @Override
public Fruit getBanana() { return new NorthBanana();
}
} package abstractFactory; public class SouthApple extends Apple {
@Override
public void get() {
System.out.println("长在南方的苹果");
}
} package abstractFactory; public class SouthBanana extends Banana {
@Override
public void get() {
System.out.println("长在南方的香蕉");
}
} package abstractFactory; public class SouthFruitFactory implements FruitFactory {
@Override
public Fruit getApple() {
return new SouthApple();
} @Override
public Fruit getBanana() {
return new SouthBanana();
}
} //测试类
package abstractFactory; public class MainClass {
public static void main(String[] args) {
FruitFactory ff = new NorthFruitFactory();
Fruit apple = ff.getApple();
apple.get(); Fruit banana = ff.getBanana();
banana.get(); System.out.println("~~~~~~~~~~~~~~~~~~~~");
FruitFactory bb = new SouthFruitFactory();
Fruit apple2 = bb.getApple();
apple2.get(); Fruit banana2 = bb.getBanana();
banana2.get(); System.out.println("~~~~~~~~~~~~~~~~~~~~");
//比如要增加室内innerApple,InnerBanana
FruitFactory cc = new InnerFruitFactory();
Fruit apple3 = cc.getApple();
apple3.get();
Fruit banana3 = cc.getBanana();
banana3.get(); }
}

模式中包含的角色及其职责

1.抽象工厂(Creator)角色
抽象工厂模式的核心,包含对多个产品结构的声明,任何工厂类都必须实现这个接口。(FruitFactory)

2.具体工厂( Concrete Creator)角色
具体工厂类是抽象工厂的一个实现,负责实例化某个产品族中的产品对象。(InnerFruitFactory,NorthFruitFactory,SouthFruitFactory)

3.抽象(Product)角色
抽象模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。(Fruit)

4.具体产品(Concrete Product)角色
抽象模式所创建的具体实例对象(NorthApple,NorthBanana,SouthApple,SouthBanana,InnerBanana,InnerFruitFactory)
总结:抽象工厂中方法对应产品结构,具体工厂对应产品族。

  在Java中,其实Spring的IOC就是一种工厂模式,通过SessionFactory来实现依赖注入。在Spring的Bean工厂中,新对象不是通过new关键字来获取的,而是通过先读取配置文件,获取配置文件中配置的类,反射生成对应的对象。

三、代理模式

  代理模式是一种常用的设计模式,百度百科中对其定义为:为其他对象提供一个代理以控制对某个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。即代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。代理类与委托类通常会存在关联关系,通常需要实现同一个接口。一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。代理模式的思想是为了提供额外的处理或者不同的操作而在实际对象与调用者之间插入一个代理对象。这些额外的操作通常需要与实际对象进行通信。

1.静态代理

静态代理:由程序员创建或者由第三方工具生成,再进行编译;在程序运行之前,代理类的.class文件已经存在了。静态代理通常只代理一个类,并且要事先知道代理的是什么。示例:假如一个班上的同学要向老师提交作业,但是老师并不直接收每个人的作业,都是通过把作业交给学习委员,再由学习委员将作业转交给老师。在这里面就是学习委员代理班上学生上交作业,学习委员就是学生的代理。

/**
* 学生接口
*/
public interface Student {
//交作业方法
void submitHomework();
}
/**
* Person实现学生类,同时也就需要重写学生的交作业方法
*/
@Data
public class Person implements Student{
private String name; public Person(String name){
this.name = name;
} @Override
public void submitHomework() {
System.out.println(name+"交作业啦!");
}
}
/**
* 学生的代理实现学生接口
*/
@Data
public class StudentProxy implements Student{ private Person person;
//构造方法,将学生对象委托给代理对象
StudentProxy(Student student){
//student.getClass()为构造方法传入的对象,判断类是不是相同
if(student.getClass() == Person.class){
//相当于学生给与了学习委员他的属性
this.person = (Person) student;
}
}
@Override
public void submitHomework() {
person.submitHomework();
}
}
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
//学生自己交作业
Student student = new Person("槑得说");
student.submitHomework(); //学习委员代交作业
Person person = new Person("有得说");
Student student2 = new StudentProxy(person);
student2.submitHomework();
}
}

  可以看到,在代理类的构造方法中我们先声明了一个学生对象student,然后再将student传给了代理对象person,最后再由person来执行交作业的方法,这就是代理模式。

其实在测试类中也可以看到,学生是可以直接交作业的,而且还简单很多,那为什么还需要代理呢?

  其实代理模式呢就是在访问实际对象时,引入了一定的间接性,不直接调用实际对象的方法,而是通过代理对象来完成。这样可以降低系统的耦合性,在上述例子中,如果老师还需要做出评价的话,就可以在不改动源码的情况下,在代理类中完成。

/**
* 学生的代理实现学生接口
*/
@Data
public class StudentProxy implements Student{ private Person zhangsan;
//构造方法,将学生对象委托给代理对象
StudentProxy(Student student){
//student.getClass()为构造方法传入的对象,判断类是不是相同
if(student.getClass() == Person.class){
//相当于学生给与了学习委员他的属性
this.zhangsan = (Person) student;
}
}
@Override
public void submitHomework() {
zhangsan.submitHomework();
System.out.println(zhangsan.getName()+"的作业完成很好!");
}
}

代理模式其优点在于:

  1. 代理模式实现使用者与真实处理者的分离,降低系统的耦合度;
  2. 调用接口时,便于扩展一些业务无关的其他操作,不影响原系统。

缺点在于:增加类代理角色,性能上比直接使用低。

2.动态代理

  Java动态代理的基本原理为:被代理对象需要实现某个接口(这是前提),代理对象会拦截对被代理对象的方法调用,在其中可以全然抛弃被代理对象的方法实现而完成另外的功能,也可以在被代理对象方法调用的前后增加一些额外的功能。动态代理可以为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

Java中常用的设计模式代码与理解的更多相关文章

  1. [ 转载 ] Java中常用的设计模式

    Java中常用的设计模式 1.单例模式 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 单例模式确保某个类只有一个 ...

  2. 设计模式学习笔记——java中常用的设计模式

    单例设计模式(Singleton Pattern) 观察者模式(Observer Pattern) 工厂模式(Factory Pattern) 策略模式(Strategy Pattern) 适配器模式 ...

  3. Java基础-Java中23种设计模式之常用的设计模式

    Java基础-Java中23种设计模式之常用的设计模式 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   一.设计模式分类 设计模式是针对特定场景给出的专家级的解决方案.总的来说设 ...

  4. Java中常用的七个阻塞队列介绍第一篇

    Java中常用的七个阻塞队列介绍第一篇 在上一篇我们对Java中的队列分类做了简单的介绍.本文咱们主要来聊聊阻塞队列中的七个常用子类.这七个阻塞队列的学习步骤:先看源码,分析完源码之后,我们再来对每个 ...

  5. Java中常用的七个阻塞队列第二篇DelayQueue源码介绍

    Java中常用的七个阻塞队列第二篇DelayQueue源码介绍 通过前面两篇文章,我们对队列有了了解及已经认识了常用阻塞队列中的三个了.本篇我们继续介绍剩下的几个队列. 本文主要内容:通过源码学习De ...

  6. Java中常用七个阻塞队列的总结

    Java队列总结 通过前面文章的学习,我们对Java中常用队列做了介绍.本文,咱们来对队列做个总结吧. 首先,我们介绍了现实生活中的实际场景(排队买票等),来告诉我们为什么需要使用队列. 队列是一种先 ...

  7. Java中常用的查找算法——顺序查找和二分查找

    Java中常用的查找算法——顺序查找和二分查找 神话丿小王子的博客 一.顺序查找: a) 原理:顺序查找就是按顺序从头到尾依次往下查找,找到数据,则提前结束查找,找不到便一直查找下去,直到数据最后一位 ...

  8. java中常用的工具类(二)

    下面继续分享java中常用的一些工具类,希望给大家带来帮助! 1.FtpUtil           Java   1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ...

  9. JAVA中常用需要设置的三个环境变量(JAVA_HOME、CLASSPATH、PATH)

    JAVA中常用需要设置的三个环境变量: JAVA_HOME.CLASSPATH.PATH (一) 配置环境变量:(相对路径) 1. JAVA_HOME=x:/jdk1.6.0 2. 用%JAVA_HO ...

随机推荐

  1. Luogu5348 密码解锁

    题面 题解 记\(N = \dfrac nm\) 这道题目就是要求\(a_m = \sum_{i=1}^N \mu(i)\mu(im)\) 因为\(\mu(ij) = \mu(i)\mu(j)[\gc ...

  2. python使用ThreadPoolExecutor每秒并发5个

    import time from concurrent.futures import ThreadPoolExecutor from functools import partial from log ...

  3. ubuntu之路——day8.1 深度学习优化算法之mini-batch梯度下降法

    所谓Mini-batch梯度下降法就是划分训练集和测试集为等分的数个子集,比如原来有500W个样本,将其划分为5000个baby batch,每个子集中有1000个样本,然后每次对一个mini-bat ...

  4. Web Worker 使用教程【转】

    原文:http://www.ruanyifeng.com/blog/2018/07/web-worker.html 一.概述 JavaScript 语言采用的是单线程模型,也就是说,所有任务只能在一个 ...

  5. 文献阅读 | Resetting histone modifications during human parental-to-zygotic transition

    Resetting histone modifications during human parental-to-zygotic transition 人类亲本-合子转变中组蛋白修饰重编程 sci-h ...

  6. 【转载】迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)

    原文链接:https://www.cnblogs.com/lixinjie/p/10811219.html 常规的误区 假设有一个展示用户详情的需求,分两步,先调用一个HTTP接口拿到详情数据,然后使 ...

  7. Fixed-Length Frames 谈谈网络编程中应用层(基于TCP/UDP)的协议设计

    http://blog.sina.com.cn/s/blog_48d4cf2d0101859x.html 谈谈网络编程中应用层(基于TCP/UDP)的协议设计 (2013-04-27 19:11:00 ...

  8. OMPL RRTConnet 生成路径和可视化

    默认规划路径算法和RRTConnet路径规划算法生成路径 1.  源代码 #include <ompl/base/SpaceInformation.h> #include <ompl ...

  9. LeetCode_326. Power of Three

    326. Power of Three Easy Given an integer, write a function to determine if it is a power of three. ...

  10. matplot中的对象

    figure:图表,可以理解为一个空间,二维情况下是一个平面 axes:坐标系,空间中的坐标系,一个空间可以有多个坐标系 axis:坐标轴,坐标系中的一个坐标轴,一个坐标轴只属于一个坐标系 画点:sc ...