首页 > 其他分享 >从实例出发,了解单例模式和静态块

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

时间:2023-04-28 17:35:45浏览次数:45  
标签:静态 private 实例 static 单例 println public out

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

单例模式的使用场景

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

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

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

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

3、频繁访问 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();
    }

    /**
    * config.properties 内容是 test.name=kite 
    */
    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 来访问实例。而且创建枚举默认就是线程安全的,并且还能防止反序列化导致重新创建新的对象。

静态块

什么是静态块呢。

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

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

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

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

他的写法是这样的:

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 函数执行中
构造代码块 执行中...
构造函数 执行中

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

1、静态代码块

2、main 函数

3、构造代码块

4、构造函数

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

除此之外,不考虑单例的情况,利用静态代码块的这个特性,可以实现其他的一些功能,例如上面提到的配置文件加载的功能,可以在类加载的时候就读取配置文件的内容,相当于一个预加载的功能,在使用的时候可以直接拿来就用。

不妨拿出手机扫一下,关注我的微信公众号,关注后可点击“加群”加入微信交流群

从实例出发,了解单例模式和静态块_静态块

人生没有回头路,珍惜当下。



标签:静态,private,实例,static,单例,println,public,out
From: https://blog.51cto.com/u_15717245/6235030

相关文章

  • Java设计模式-单例模式
    一、前言单例模式是一种设计模式,它确保一个类只能创建一个实例,并提供一种全局访问这个实例的方式。在Java中,单例模式可以通过多种方式来实现,其中最常见的是使用私有构造函数和静态方法实现二、基本语法在Java中,实现单例模式的方式有多种,其中最常见的实现方式包括以下几种:1、......
  • Java ByteBuffer 使用实例
    packagefoo;importjava.io.IOException;importjava.nio.ByteBuffer;importjava.nio.CharBuffer;importjava.nio.charset.Charset;/**ByteBuffer并且这三个指针的关系是position<=limit<=capacity.position是当前读写的位置。limit是最大能读写的......
  • 单例模式的几种写法(包含双检锁写法)
    饿汉式单例类1.publicclass2.{3.private4.5.}6.7.privatestaticSingletoninstance=new8.9.privatestatic10.return11.}12.} 饿汉式提前实例化,没有懒汉式中多线程问题,但不管我们是不是调用getInstance()都会存在一个......
  • Python单例的常用几种实现方法
    这两天在看自己之前写的代码,所以正好把用过的东西整理一下,单例模式,在日常的代码工作中也是经常被用到,所以这里把之前用过的不同方式实现的单例方式整理一下。装饰器的方式这种方式也是工作中经常用的一种,用起来也比较方便,代码实现如下defSingleton(cls):_instance={......
  • 单例模式
    一、线程安全性的讲解1、视频截图 2、线程安全性的代码加不加临界区进行验证1//!!!!!!!!!加C++泛型编程与STL开发实战QQ群:726114806下载代码和交流2#include<afxwin.h>3#include<iostream>4#include<stdio.h>5usingnamespacestd;67CRITICAL_SECTIONg_......
  • 静态数据成员
    用关键字static声名为该类所有对象共享必须在类外定义和初始化 原理1.同一类的不同对象,其成员数据之间是互相独立的。2.当我们将类的某一个数据成员的声名为static,则油该类所生产的所有对象,其静态成员共享一个存储空间 static规定1.static成员必须在类外定义并初始化2......
  • 三种实例化bean的方式
    知识点:【1.使用类构造器实例化<beanid=“orderService"class="cn.itcast.OrderServiceBean"/>2.使用静态工厂方法实例化<beanid="personService"class="cn.itcast.service.OrderFactory"factory-method="cr......
  • 静态初始化器知多少
    提问静态初始化器有什么特点回答只执行一次,在首次调用时执行2.可以用来初始化变量,并封装异常回答......
  • MSTP-多实例生成树
    RSTP与STP的缺点同一局域网内所有的vlan共享一个生成树,无法在vlan间实现数据流量的负载均衡;链路利用率低,被阻塞的冗余链路不承载任何流量,造成了带宽的浪费,还可能造成部分vlan报文无法转发。MSTP:MSTP兼容STP和RSTP,既可以快速收敛,又能使不同VLAN的流量沿各自的路径转发,从而为冗......
  • 虚拟机配置静态IP地址
       子网IP可以随意设置子网掩码一定要是255.255.255.0网关IP(G)设置成【X.X.X.2】 添加以下五段代码。❗注意:网关地址和DNS服务器要和⑥一样。❗注意:IPADDR静态IP地址和④⑥要不一样,此处有错误,后面我改成了192.168.88.88打开以下文件请查看底部 执行以下......