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. Requests库的主要方法:requests.request为requests.get和requests.post两个的汇总,只是需要传方法

    1. requests.request(method,url,**kwargs) method:请求方式,对应get/put/post等七种 :拟获取页面的url链接 :控制访问参数,共13个 met ...

  2. 【多线程与并发】Java中的12个原子操作类

    从JDK1.5开始,Java提供了java.util.concurrent.atomic包,该包中的原子操作类提供了一种使用简单.性能高效(使用CAS操作,无需加锁).线程安全地更新一个变量的方式. ...

  3. linux10.日志服务器建立和克隆机的网卡问题

    日志服务器建立       克隆虚拟机网卡混乱问题处理           vim /etc/udev/rules.d/70-perisistent-net.rules 调整mac地址与设备的对应关系 ...

  4. 解决:WdatePicker新增状态下只能取比当前月份大的月份,编辑状态下只能取比当前input里指定月份的月份大的值

    onclick="WdatePicker({ dateFmt: 'yyyy-MM', autoPickDate: true, minDate: this.value==''?'%y-#{%M ...

  5. Microservices in action: java(spring) and .net

    Manning | Homehttps://www.manning.com/ What is a Microservice? | Manninghttps://freecontent.manning. ...

  6. android studio 把 ionic 打包时修改应用名称、修改应用图标、修改启动画面,升级打包

    在项目中resources中替换成自己的图标和启动画面即可 在config.xml 修改包名 打正式包和升级打包同原生的类似,在Androidmanifest.xml修改版本号和版本名

  7. VPB编译日志2

    1>------ 已启动全部重新生成: 项目: ZERO_CHECK, 配置: Debug x64 ------1> Checking Build System1> CMake do ...

  8. 修复gitlab服务器突然停电导致PostgreSQL损坏的数据库

    最开始是存储的卷组受损,使用的DRBD,使用了xfs分区格式: 挂载也报错: mount /dev/drbd0 /var/opt mount: wrong fs type, bad option, b ...

  9. Java编程实战宝典PDF (中文版带书签)

    Java编程实战宝典PDF 目录 第1篇 Java基础知识入门第1章 Java的开发运行环境( 教学视频:57分钟)1.1 Java运行原理与Java虚拟机1.1.1 Java运行原理简述1.1.2 ...

  10. LeetCode_345. Reverse Vowels of a String

    345. Reverse Vowels of a String Easy Write a function that takes a string as input and reverse only ...