我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何在Unity3D中使用单例模式和静态类

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何在Unity3D中使用单例模式和静态类

本篇文章为大家展示了如何在Unity3D中使用单例模式和静态类,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

静态类不能继承和被继承!(严格点说是只能继承System.Object)也就是说你的静态类不可能去继承MonoBehaviour,不能实现接口。

静态方法不能使用非静态成员!如果你大量使用静态方法,而方法里又需要用到这个类的成员,那么你的成员得是静态成员。

第2点需要注意:如果你想在Unity的编辑器下调整某个参数,那么这个参数就不能是静态的(哪怕你自定义EditorWindow去修改这个值也没用),解决的办法是通过UnityEngine.ScriptableObject去存放配置(生成*.asset文件),然后在运行中通过LoadAsset去加载,然后再改变静态成员。至于原因,相信不难理解——你看到的所有Unity组件都是一个个实例,你要通过Unity的编辑器去配置,那么你就得有一个这样的可配置实例。

从面向对象上想一下:静态方法或者静态类,不需要依赖对象,类是唯一的;单例的静态实例,一般就是唯一的一个对象(当然也可以有多个)。差别嘛。。。好像也不大。。。

如果这样考虑没有错,那再回头比较一下两种方式:

静态(静态方法或者静态类),代码编写上绊手绊脚,方法调用很方便,运行效率高一丢丢。逻辑面向过程,不能很好地控制加载和销毁。

单例(类的静态实例),代码编写和其他类完全一样,继承抽象模版接口都可以,Unity里也很方便进行参数配置,不过使用麻烦有犯错的可能性(必须通过实例调用方法),效率不如静态(但是也不会有很大影响吧)。

如果这些说法太抽象,那我再给出一个常见的问题:如果你的框架有一个SoundManager能够管理所有的声音播放,那么你会怎么去实现?

(在刚接触AudioSource这个组件的时候,我想的是每一个声音都由一个AudioSource去播放。但是后来发现完全没必要,AudioSource有静态的PlayClipAtPoint方法去播放临时3D音效,同时有实例方法PlayOneShot去播放临时音效(2D和3D取决于当实例的SpatialBlend)。如果没有特殊的需求,那么一个AudioSource循环播放背景音乐,上述两种方法播放游戏中的特效音频,这对于大部分游戏已经足够了。)

那么问题来了:你的SoundManager播放声音的方法如果是静态的,那么AudioSource组件必须在代码中通过各种方式去获取(新建组件或者获取特定GameObject下的组件)——因为保存这个组件的变量必须是静态的,也就不能通过Unity的编辑器去赋值。如果不去阅读代码那么用户完全不知道这是一个什么样的组件获取流程,如果我破坏这个流程(同名物体,包含互斥组件等),那么这个Manager很有可能会出现不可预料的异常。

而继承MonoBehaviour并RequireComponent(typeof(AudioSource)),怎么看也比“为了静态而静态”的代码要方便健壮的多。

实际上到这里已经可以基本总结出何时需要使用单例了:

只要你的类需要保存其他组件作为变量,那么就有必要使用单例;

只要你有在Unity编辑器上进行参数配置的需求,那么就有必要使用单例;

只要你的管理器需要进行加载的顺序控制,那么就有必要使用单例(比如热更新之后加载ResourcesManager);

当然,这里都只是“有必要”,并不是“必须”。两者区别最大的地方,一个是方便写,一个是方便用。方便写的代价是每次调用加个instance,方便用的代价则是放弃了面向对象和Unity的“所见即所得”,孰轻孰重,自己抉择。

另一方面,和“为了静态而静态”一样,“为了单例而单例”同样是一个不合理的设计。这样的解释仍然是那么的模糊,那么,就给自己定义一个最简单的规则吧——如果你的单例类里没有任何需要保存状态的变量,那么这个类里的方法就可以全都是静态方法,这个类也可以是个静态类。

补充:从实例出发,了解单例模式和静态块

就算你没有用到过其他的设计模式,但是单例模式你肯定接触过,比如,Spring 中 bean 默认就是单例模式的,所有用到这个 bean 的实例其实都是同一个。

单例模式的使用场景

什么是单例模式呢,单例模式(Singleton)又叫单态模式,它出现目的是为了保证一个类在系统中只有一个实例,并提供一个访问它的全局访问点。从这点可以看出,单例模式的出现是为了可以保证系统中一个类只有一个实例而且该实例又易于外界访问,从而方便对实例个数的控制并节约系统资源而出现的解决方案。

使用单例模式当然是有原因,有好处的了。在下面几个场景中适合使用单例模式:

有频繁实例化然后销毁的情况,也就是频繁的 new 对象,可以考虑单例模式;

创建对象时耗时过多或者耗资源过多,但又经常用到的对象;

频繁访问 IO 资源的对象,例如数据库连接池或访问本地文件;

下面举几个例子来说明一下:

1、网站在线人数统计;

其实就是全局计数器,也就是说所有用户在相同的时刻获取到的在线人数数量都是一致的。要实现这个需求,计数器就要全局唯一,也就正好可以用单例模式来实现。当然这里不包括分布式场景,因为计数是存在内存中的,并且还要保证线程安全。下面代码是一个简单的计数器实现。

public class Counter {       private static class CounterHolder{        private static final Counter counter = new Counter();    }    private Counter(){        System.out.println("init...");    }    public static final Counter getInstance(){        return CounterHolder.counter;    }    private AtomicLong online = new AtomicLong();    public long getOnline(){        return online.get();    }    public long add(){        return online.incrementAndGet();    }}

2、配置文件访问类;

项目中经常需要一些环境相关的配置文件,比如短信通知相关的、邮件相关的。比如 properties 文件,这里就以读取一个properties 文件配置为例,如果你使用的 Spring ,可以用 @PropertySource 注解实现,默认就是单例模式。如果不用单例的话,每次都要 new 对象,每次都要重新读一遍配置文件,很影响性能,如果用单例模式,则只需要读取一遍就好了。以下是文件访问单例类简单实现:

public class SingleProperty {    private static Properties prop;    private static class SinglePropertyHolder{        private static final SingleProperty singleProperty = new SingleProperty();    }        private SingleProperty(){        System.out.println("构造函数执行");        prop = new Properties();        InputStream stream = SingleProperty.class.getClassLoader()                .getResourceAsStream("config.properties");        try {            prop.load(new InputStreamReader(stream, "utf-8"));        } catch (IOException e) {            e.printStackTrace();        }    }    public static SingleProperty getInstance(){        return SinglePropertyHolder.singleProperty;    }         public String getName(){        return prop.get("test.name").toString();    }    public static void main(String[] args){        SingleProperty singleProperty = SingleProperty.getInstance();        System.out.println(singleProperty.getName());    }}

3、数据库连接池的实现,也包括线程池。

为什么要做池化,是因为新建连接很耗时,如果每次新任务来了,都新建连接,那对性能的影响实在太大。所以一般的做法是在一个应用内维护一个连接池,这样当任务进来时,如果有空闲连接,可以直接拿来用,省去了初始化的开销。

所以用单例模式,正好可以实现一个应用内只有一个线程池的存在,所有需要连接的任务,都要从这个连接池来获取连接。

如果不使用单例,那么应用内就会出现多个连接池,那也就没什么意义了。如果你使用 Spring 的话,并集成了例如 druid 或者 c3p0 ,这些成熟开源的数据库连接池,一般也都是默认以单例模式实现的。

单例模式的实现方法

如果你在书上或者网站上搜索单例模式的实现,一般都会介绍5、6中方式,其中有一些随着 Java 版本的升高,以及多线程技术的使用变得不那么实用了,这里就介绍两种即高效,而且又是线程安全的方式。

1. 静态内部类方式

public class Singleton {      private static class SingletonHolder {          private static final Singleton INSTANCE = new Singleton();      }      private Singleton (){}      public static final Singleton getInstance() {          return SingletonHolder.INSTANCE;     }  }

这种写法仍然使用 JVM 本身机制保证了线程安全问题,由于 SingletonHolder 是私有的,除了 getInstance() 方法外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。上面的两个例子就是用这种方式实现的。

2. 枚举方式

public enum SingleEnum {    INSTANCE;    SingleEnum(){        System.out.println("构造函数执行");    }    public String getName(){        return "singleEnum";    }    public static void main(String[] args){        SingleEnum singleEnum = SingleEnum.INSTANCE;        System.out.println(singleEnum.getName());    }}

我们可以通过 SingleEnum.INSTANCE 来访问实例。而且创建枚举默认就是线程安全的,并且还能防止反序列化导致重新创建新的对象。

静态块

什么是静态块呢

它是随着类的加载而执行,只执行一次,并优先于主函数。具体说,静态代码块是由类调用的。类调用时,先执行静态代码块,然后才执行主函数的;

静态代码块其实就是给类初始化的,而构造代码块是给对象初始化的;

静态代码块中的变量是局部变量,与普通函数中的局部变量性质没有区别;

一个类中可以有多个静态代码块;

他的写法是这样的:

static {        System.out.println("static executed");    }

来看一下下面这个完整的实例:

public class SingleStatic {    static {        System.out.println("static 块执行中...");    }    {        System.out.println("构造代码块 执行中...");    }    public SingleStatic(){        System.out.println("构造函数 执行中");    }    public static void main(String[] args){        System.out.println("main 函数执行中");        SingleStatic singleStatic = new SingleStatic();    }}

他的执行结果是这样的:

static 块执行中...

main 函数执行中

构造代码块 执行中...

构造函数 执行中

从中可以看出他们的执行顺序分别为:

静态代码块

main 函数

构造代码块

构造函数

利用静态代码块只在类加载的时候执行,并且只执行一次这个特性,也可以用来实现单例模式,但是不是懒加载,也就是说每次类加载就会主动触发实例化。

上述内容就是如何在Unity3D中使用单例模式和静态类,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何在Unity3D中使用单例模式和静态类

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何在Unity3D中使用单例模式和静态类

本篇文章为大家展示了如何在Unity3D中使用单例模式和静态类,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。1、静态类不能继承和被继承!(严格点说是只能继承System.Object)也就是说你的静
2023-06-14

java静态内部类单例模式如何实现

在Java中,静态内部类的单例模式可以通过以下步骤实现:创建一个私有的静态内部类,命名为SingletonHolder,并在该类中声明一个私有的静态final的外部类实例。静态内部类的定义如下:private static class Si
2023-10-24

Java中静态内部类怎么利用单例模式实现

本篇文章为大家展示了Java中静态内部类怎么利用单例模式实现,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。Singleton是众多设计模式中最容易理解的一种,也是众多设计模式中较为重要的一种设计模式
2023-05-31

如何在 Linux 中使用动态和静态库

本篇内容介绍了“如何在 Linux 中使用动态和静态库”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!了解 Linux 如何使用库,包括静态库
2023-06-15

关于如何在Python中使用静态、类或

Python中方法的工作方式方法是存储在类属性中的函数,你可以用下面这种方式声明和访问一个函数>>> class Pizza(object): ... def __init__(self, size): ...
2023-01-31

PHP中如何使用单例模式?

php 中的单例模式确保一个类只有一个实例,通过以下步骤实现:创建私有静态属性存储实例。创建私有构造函数防止直接实例化。创建公共静态方法用于获取实例;如果不存在则创建并存储为私有属性。PHP中的单例模式简介单例模式是设计模式的一种,用于
PHP中如何使用单例模式?
2024-05-21

关于如何在Python中使用静态、类或抽

方法是存储在类属性中的函数,你可以用下面这种方式声明和访问一个函数 >>> class Pizza(object): ... def __init__(self, size): ... self.s
2023-01-30

如何使用Python元类实现单例模式

这篇文章主要介绍了如何使用Python元类实现单例模式,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。元类实现:class MetaClass(type): def __
2023-06-17

如何使用Python中的@classmethod实现单例模式

这篇文章主要介绍了如何使用Python中的@classmethod实现单例模式,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。@classmethod实现单例模式:class
2023-06-17

如何使用PHP7的匿名类实现简单的单例设计模式?

如何使用PHP7的匿名类实现简单的单例设计模式?在PHP开发中,单例设计模式被广泛应用于需要确保类的实例只存在一个的场景。而PHP7引入的匿名类使得实现单例模式变得更加简单和优雅。本文将介绍如何利用PHP7的匿名类来实现简单的单例设计模式,
2023-10-22

如何使用PHP7的匿名类实现更加灵活的单例模式?

如何使用PHP7的匿名类实现更加灵活的单例模式?单例模式是一种常用的设计模式,它保证一个类只有一个实例,并且提供一个全局的访问点。在PHP中,实现单例模式通常使用静态变量和方法来保证只创建一个实例。然而,在PHP7中,我们可以使用匿名类来实
2023-10-22

如何在Netdata中结合使用静态和动态阈值进行高级告警配置

在Netdata中,可以结合使用静态和动态阈值进行高级告警配置。以下是具体步骤:静态阈值配置:进入Netdata的配置文件目录,通常位于/etc/netdata/。找到health.d目录并进入,这里存放着各种检测插件的配置文件。在he
如何在Netdata中结合使用静态和动态阈值进行高级告警配置
2024-06-03

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录