Java 设计模式系列:享元模式

这篇具有很好参考价值的文章主要介绍了Java 设计模式系列:享元模式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

简介

享元模式(Flyweight Pattern)是一种软件设计模式,用于减少内存使用和提高性能。它通过共享细粒度对象来减少创建和销毁对象时所需的内存。享元模式适用于大量相似对象的场景,这些对象可以共享相同的状态和行为。

享元模式的核心思想是将对象分为内部状态和外部状态。内部状态是对象自身的状态,通常是不可变的,可以共享;而外部状态则是与对象相关联的环境信息,通常是变化的,由客户端传入享元对象内部。通过共享内部状态,可以显著减少系统中的对象数量,从而降低内存消耗。

结构

享元模式包含以下几个角色:

  1. 抽象享元类(Flyweight):通常是一个接口或抽象类,声明一些可以向外界提供享元对象内部状态的方法,同时也可以通过这些方法设置内部状态。
  2. 具体享元类(ConcreteFlyweight):实现了抽象享元类,为内部状态提供了存储空间。
  3. 非共享具体享元类(UnsharedConcreteFlyweight):并不是所有的抽象享元类的子类都需要被共享,不能被共享的子类可以设计为非共享具体享元类,当需要使用非共享具体享元类时可以直接实例化创建。
  4. 享元工厂类(FlyweightFactory):享元工厂类用于创建并管理享元对象,将各个类型的具体享元对象存储在一个享元池中,享元池一般设计为键值对集合结构。

标准的享元模式结构包含可以共享的具体享元类和不可以共享的非共享具体享元类。根据具体情况,可以分为单纯享元模式和复合享元模式。单纯享元模式中所有具体享元类都是可以共享的,而复合享元模式则将一些单纯享元对象使用组合模式加以组合,形成复合享元对象。

案例实现

例】俄罗斯方块

下面的图片是众所周知的俄罗斯方块中的一个个方块,如果在俄罗斯方块这个游戏中,每个不同的方块都是一个实例对象,这些对象就要占用很多的内存空间,下面利用享元模式进行实现。

Java 设计模式系列:享元模式,java,设计模式,享元模式

先来看类图:

Java 设计模式系列:享元模式,java,设计模式,享元模式

代码如下:

俄罗斯方块有不同的形状,我们可以对这些形状向上抽取出AbstractBox,用来定义共性的属性和行为。

public abstract class AbstractBox {
    public abstract String getShape();

    public void display(String color) {
        System.out.println("方块形状:" + this.getShape() + " 颜色:" + color);
    }
}

接下来就是定义不同的形状了,IBox类、LBox类、OBox类等。

public class IBox extends AbstractBox {

    @Override
    public String getShape() {
        return "I";
    }
}

public class LBox extends AbstractBox {

    @Override
    public String getShape() {
        return "L";
    }
}

public class OBox extends AbstractBox {

    @Override
    public String getShape() {
        return "O";
    }
}

提供了一个工厂类(BoxFactory),用来管理享元对象(也就是AbstractBox子类对象),该工厂类对象只需要一个,所以可以使用单例模式。并给工厂类提供一个获取形状的方法。

public class BoxFactory {

    private static HashMap<String, AbstractBox> map;

    private BoxFactory() {
        map = new HashMap<String, AbstractBox>();
        AbstractBox iBox = new IBox();
        AbstractBox lBox = new LBox();
        AbstractBox oBox = new OBox();
        map.put("I", iBox);
        map.put("L", lBox);
        map.put("O", oBox);
    }

    public static final BoxFactory getInstance() {
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        private static final BoxFactory INSTANCE = new BoxFactory();
    }

    public AbstractBox getBox(String key) {
        return map.get(key);
    }
}

优缺点和使用场景

1,优点:

  1. 降低内存消耗:通过共享内部状态,可以显著减少系统中的对象数量,从而降低内存消耗。
  2. 提高性能:由于减少了对象的创建和销毁次数,可以提高系统的性能。
  3. 提高复用性:通过将对象的状态分离出来,可以使多个对象共享相同的状态,提高了代码的复用性。

2,缺点:

  1. 实现复杂度较高:需要分离出内部状态和外部状态,使得程序逻辑复杂化。
  2. 需要分离出共享状态和非共享状态:为了实现对象的共享,需要将对象的共享状态和非共享状态进行分离,这可能会增加代码的复杂度。

3,使用场景:

  • 一个系统有大量相同或者相似的对象,造成内存的大量耗费。
  • 对象的大部分状态都可以外部化,可以将这些外部状态传入对象中。
  • 在使用享元模式时需要维护一个存储享元对象的享元池,而这需要耗费一定的系统资源,因此,应当在需要多次重复使用享元对象时才值得使用享元模式。

源码中的应用

Spring中

在Spring框架中,享元模式的应用主要体现在以下几个方面:

  1. Bean的作用域管理:Spring框架中的Bean默认是单例的,这意味着在整个Spring容器中,每个Bean的ID都对应着一个唯一的实例对象。这种单例模式实际上是享元模式的一种应用。通过共享相同的Bean实例,减少了对象的创建和销毁,从而节约了系统资源,提高了应用程序的性能。
  2. 事件处理机制:在Spring的事件处理机制中,享元模式被用来管理事件监听器对象的创建和销毁。通过共享已经存在的监听器对象,避免了大量相似对象的创建,从而节省了系统资源,提高了事件处理的效率。
  3. BeanFactory管理:在Spring的BeanFactory中,也使用了享元模式来管理Bean对象的创建和销毁。BeanFactory负责实例化、配置和管理Bean,通过共享Bean的实例,实现了对象的复用,进一步提高了系统资源的利用率。
  4. 缓存机制:在Spring Security等模块中,权限信息等数据经常被缓存以提高性能。这些缓存的实例在需要时被创建,并在多个上下文中共享,这也是享元模式的一种应用。通过共享相同的权限对象实例,减少了对象的创建和内存占用,提高了系统的响应速度。

JDK中

享元模式在JDK中的应用主要体现在一些系统库和组件的设计中,旨在通过共享对象来减少内存使用和提高性能。下面是一些具体的例子:

  1. 字符串常量池:在JDK中,字符串常量是通过享元模式来管理的。当我们使用双引号创建字符串字面量时,JVM会首先检查字符串常量池中是否存在相同的字符串。如果存在,则返回该字符串的引用;如果不存在,则在常量池中创建一个新的字符串对象,并返回其引用。这种机制减少了相似字符串对象的创建,节省了内存空间。

  2. 包装类缓存:Java中的包装类(如Integer、Boolean等)对于小范围的数值,如Integer在-128到127之间,使用了享元模式。这些范围内的对象会被缓存起来,当需要创建这些范围内的对象时,会直接从缓存中获取,而不是重新创建。这种机制减少了包装类对象的创建,提高了性能。

    例:java.lang.Integer

    https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/lang/Integer.java

    public class Demo {
        public static void main(String[] args) {
            Integer i1 = 127;
            Integer i2 = 127;
    
            System.out.println("i1和i2对象是否是同一个对象?" + (i1 == i2));
    
            Integer i3 = 128;
            Integer i4 = 128;
    
            System.out.println("i3和i4对象是否是同一个对象?" + (i3 == i4));
        }
    }
    

    运行上面代码,结果如下:

Java 设计模式系列:享元模式,java,设计模式,享元模式

为什么第一个输出语句输出的是true,第二个输出语句输出的是false?通过反编译软件进行反编译,代码如下:

public class Demo {
    public static void main(String[] args) {
        Integer i1 = Integer.valueOf((int)127);
        Integer i2 Integer.valueOf((int)127);
        System.out.println((String)new StringBuilder().append((String)"i1\u548ci2\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i1 == i2)).toString());
        Integer i3 = Integer.valueOf((int)128);
        Integer i4 = Integer.valueOf((int)128);
        System.out.println((String)new StringBuilder().append((String)"i3\u548ci4\u5bf9\u8c61\u662f\u5426\u662f\u540c\u4e00\u4e2a\u5bf9\u8c61\uff1f").append((boolean)(i3 == i4)).toString());
    }
}

上面代码可以看到,直接给Integer类型的变量赋值基本数据类型数据的操作底层使用的是 valueOf() ,所以只需要看该方法即可

public final class Integer extends Number implements Comparable<Integer> {
    
	public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    
    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                }
            }
            high = h;
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
}

可以看到 Integer 默认先创建并缓存 -128 ~ 127 之间数的 Integer 对象,当调用 valueOf 时如果参数在 -128 ~ 127 之间则计算下标并从缓存中返回,否则创建一个新的 Integer 对象。

  1. 集合框架中的对象复用:在JDK的集合框架中,有些实现也采用了享元模式的思想。例如,在HashMap等集合类中,为了优化性能,可能会复用某些内部对象,而不是每次都创建新的对象。

  2. 线程池:JDK中的线程池(如ExecutorService)也是享元模式的一种应用。线程池通过复用已有的线程,减少了线程的创建和销毁开销,提高了系统的并发处理能力。文章来源地址https://www.toymoban.com/news/detail-851717.html

到了这里,关于Java 设计模式系列:享元模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java设计模式之结构型-享元模式(UML类图+案例分析)

    目录 一、基本概念 二、UML类图 三、角色设计 四、案例分析 4.1、基本实现 4.2、游戏角色 五、总结 享元模式是一种结构型设计模式,主要用于减少创建大量相似对象所占用的内存,它通过共享技术来有效支持大量细粒度的对象。 角色 描述 抽象享元角色 定义出对象的外部状

    2024年02月16日
    浏览(45)
  • 【设计模式——学习笔记】23种设计模式——享元模式Flyweight(原理讲解+应用场景介绍+案例介绍+Java代码实现)

    你的公司主要做一些小型的外包项目,之前给客户A做一个产品展示网站,客户A的朋友感觉效果不错,也希望做这样的产品展示网站,但是要求有些不同 有客户要求以新闻的形式发布 有客户要求以博客的形式发布 有客户要求以微信公众号的形式发布 【 传统方式】 直接复制

    2024年02月15日
    浏览(51)
  • 【Java面试题】设计模式之七种结构性模式——代理模式、适配器模式、桥接模式、装饰模式、外观模式、享元模式、组合模式

    目录 一、代理模式 二、适配器模式 三、桥接模式 四、装饰模式 五、外观模式 六、享元模式 七、组合模式 概念: 代理模式是为其他对象提供一种以代理控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对

    2023年04月09日
    浏览(50)
  • 软件设计模式系列之十三——享元模式

    享元模式(Flyweight Pattern)是一种结构型设计模式,它旨在减少内存占用或计算开销,通过共享大量细粒度对象来提高系统的性能。这种模式适用于存在大量相似对象实例,但它们的状态可以外部化(extrinsic),并且可以在多个对象之间共享的情况。 为了更好地理解享元模式

    2024年02月08日
    浏览(52)
  • 【享元设计模式详解】C/Java/JS/Go/Python/TS不同语言实现

    享元模式(Flyweight Pattern),是一种结构型设计模式。主要用于减少创建对象的数量,以减少内存占用和提高性能。它摒弃了在每个对象中保存所有数据的方式,通过共享多个对象所共有的相同状态,让你能在有限的内存容量中载入更多对象。 当程序需要生成数量巨大的相似

    2023年04月10日
    浏览(36)
  • Java设计模式系列--观察者模式写法2:JDK

    原文网址:Java设计模式系列--观察者模式写法2:JDK_IT利刃出鞘的博客-CSDN博客 说明 本文用示例介绍观察者模式的一种写法:JDK。 JDK的观察者模式简介 在 Java 中,java.util.Observable 类和 java.util.Observer 接口定义了观察者模式,只要实现这两个接口就可以编写观察者模式。 1. Ob

    2024年02月13日
    浏览(37)
  • 设计模式——享元模式

    享元模式(Flyweight Pattern)是池技术的重要实现方式。 使用共享对象可以有效地支持大量的细粒度对象。 优点 可以大大减少应用程序创建对象的数量,降低程序内存占用。 缺点 提高了系统的复杂度,需要分离出享元对象的外部状态(key)和内部状态(对象属性),并且外部

    2024年02月16日
    浏览(38)
  • 设计模式:享元模式

    首先我们需要简单了解一下什么是享元模式。 享元模式(Flyweight Pattern) :主要用于减少创建对象的数量,以减少内存占用和提高性能。享元模式的重点就在这个享字,通过一些共享技术来减少对象的创建,实际上Java中String值的存储,Volley中的 ByteArrayPool 也使用到了享元模式,

    2024年02月08日
    浏览(44)
  • 设计模式-享元模式

    享元模式(Flyweight Pattern)是一种结构型设计模式,主要用于减少创建大量相似对象对内存资源的消耗,通过共享这些对象来提高程序性能和系统资源利用率。在实际应用场景中string就是使用了享元模式,string a = “123”; string b = “123”; 我们假设有一个场景是需要创建大量的

    2024年01月24日
    浏览(42)
  • 设计模式(22)享元模式

    一、介绍: 1、定义:享元模式(Flyweight Pattern)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结构的方式。 2、组成结构: (1)Flyweight(抽象享元类):通常是一个接口或

    2024年02月07日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包